chore(web): guard handwritten requests with native lint rules (#44436)

This commit is contained in:
Stephen Zhou authored and GitHub committed 2026-10-20 13:57:11 +00:00
1 parent 8197cb6503
commit 201f46f19a
6 files changed
+1240 -217

No files matched your search

File diff suppressed because it is too large. Load diff
+35
View File
@@ -156,6 +156,41 @@ Explain the concrete reason after `--`: which external contract, lifecycle, or r
`reportUnusedDisableDirectives` runs at `error` repository-wide. Remove an exception when the finding no longer exists. Keep both checks active: a described disable may still be unused, and a used disable may still lack a reason.
### Handwritten HTTP Calls
Web uses native Oxlint rules to discourage deprecated APIs and new handwritten requests:
- `typescript/no-deprecated` rejects references to deprecated APIs. Legacy base/fetch
transport entry points carry `@deprecated`; generated `consoleClient`, `consoleQuery`,
and marketplace clients remain allowed, including direct calls from UI components.
- `no-restricted-globals` and `no-restricted-properties` reject direct global
`fetch` / `XMLHttpRequest` references, including `window`, `globalThis`, `global`,
and `self` property access.
- `no-restricted-imports` rejects new value imports from common HTTP packages
(including Ky, Axios, Undici and Node http/https). Type-only imports remain allowed.
Existing legacy-service and localStorage import/access restrictions remain active.
`typescript/no-deprecated` applies throughout Web, including tests, mocks, stories,
test infrastructure, and maintenance scripts. The raw transport and HTTP-client import
restrictions apply only to production code. Repository-wide generated/vendor and
declaration-file ignores still apply. There is no custom API rule or data-flow tracker.
Existing findings are recorded in `oxlint-suppressions.json`. These are counts of
references/imports, not endpoints awaiting migration: they include transport internals,
assets, special transports, and unrelated deprecated APIs reported by `no-deprecated`.
Prune counts when removing existing findings; do not raise them for new work.
For a necessary new special transport, use a scoped `oxlint-disable-next-line`
for the applicable native rule with a concrete contract reason.
The simpler native checks intentionally accept gaps: for a deprecated arrow-function
helper, current Oxlint reports `const saved = get` at assignment but does not propagate
deprecation to later `saved()` calls. Adding calls through an existing alias,
HTTP-client import, or XHR instance may therefore pass its existing baseline.
Dynamic property access and indirect loading also require review. A per-file count
cannot detect replacing one old use with another at the same count. Do not treat
passing lint as proof that all requests use generated contracts. This rule set
does not enforce Query ownership.
### Translation Function Types
`dify/require-i18n-namespace` requires translation hook calls to use non-empty
+192
View File
@@ -0,0 +1,192 @@
import { spawnSync } from 'node:child_process'
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { createRequire } from 'node:module'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { expect, it } from 'vite-plus/test'
import { lintConfig } from './lint.config.ts'
type Diagnostic = { code?: string; filename: string; labels: { span: { line: number } }[] }
const nativeRules = new Set([
'typescript/no-deprecated',
'no-restricted-globals',
'no-restricted-properties',
'no-restricted-imports',
])
function fixture() {
const require = createRequire(import.meta.url)
const viteRequire = createRequire(require.resolve('vite-plus/package.json'))
const oxlintBin = join(dirname(dirname(viteRequire.resolve('oxlint'))), 'bin', 'oxlint')
const oxlintRequire = createRequire(oxlintBin)
const tsgolintRequire = createRequire(oxlintRequire.resolve('oxlint-tsgolint/package.json'))
const tsgolint = tsgolintRequire.resolve(
`@oxlint-tsgolint/${process.platform}-${process.arch}/tsgolint${process.platform === 'win32' ? '.exe' : ''}`,
)
const directory = mkdtempSync(join(tmpdir(), 'dify-native-http-'))
writeFileSync(
join(directory, 'tsconfig.json'),
JSON.stringify({
compilerOptions: { target: 'ESNext', module: 'ESNext', lib: ['ESNext', 'DOM'], strict: true },
include: ['web/**/*.ts', 'web/**/*.tsx'],
}),
)
mkdirSync(join(directory, 'web/service'), { recursive: true })
writeFileSync(
join(directory, 'web/service/legacy.ts'),
'/** @deprecated Use generated clients. */\nexport const get = (url: string) => Promise.resolve(url)\n',
)
const select = (rules: Record<string, unknown>) =>
Object.fromEntries(Object.entries(rules).filter(([name]) => nativeRules.has(name)))
writeFileSync(
join(directory, '.oxlintrc.json'),
JSON.stringify({
categories: { correctness: 'off' },
options: { typeAware: true },
rules: select(lintConfig.rules),
overrides: lintConfig.overrides.map((override) => ({
...override,
rules: select(override.rules),
})),
}),
)
return {
close: () => rmSync(directory, { recursive: true, force: true }),
lint(source: string, file = 'web/feature.ts', flags: string[] = []) {
mkdirSync(dirname(join(directory, file)), { recursive: true })
writeFileSync(join(directory, file), source)
const result = spawnSync(
process.execPath,
[oxlintBin, '--config', '.oxlintrc.json', '--format', 'json', ...flags, file],
{
cwd: directory,
encoding: 'utf8',
env: { ...process.env, OXLINT_TSGOLINT_PATH: tsgolint },
},
)
expect(result.error).toBeUndefined()
expect([0, 1], result.stderr + result.stdout).toContain(result.status)
return {
status: result.status,
diagnostics: JSON.parse(result.stdout).diagnostics as Diagnostic[],
}
},
}
}
it('rejects each direct deprecated helper call and allows generated clients and local functions', () => {
const test = fixture()
try {
const result = test.lint(
[
"import { get as read } from './service/legacy'",
"import * as legacy from './service/legacy'",
"read('/one')",
"legacy.get('/two')",
'const consoleClient = { get: (url: string) => Promise.resolve(url) }',
"consoleClient.get('/allowed')",
'const fetch = (url: string) => url',
"fetch('/local')",
].join('\n'),
)
expect(result.diagnostics.map((d) => [d.code, d.labels[0]!.span.line])).toEqual([
['typescript(no-deprecated)', 3],
['typescript(no-deprecated)', 4],
])
} finally {
test.close()
}
})
it('blocks native requests and client imports without blocking type-only imports', () => {
const test = fixture()
try {
const result = test.lint(
[
"import ky from 'ky'",
"import type { Options } from 'ky'",
"fetch('/one')",
"window.fetch('/two')",
"globalThis['fetch']('/three')",
'new XMLHttpRequest()',
'new self.XMLHttpRequest()',
"import { request } from 'node:https'",
].join('\n'),
)
expect(result.diagnostics.map((d) => d.labels[0]!.span.line).sort((a, b) => a - b)).toEqual([
1, 3, 4, 5, 6, 7, 8,
])
} finally {
test.close()
}
})
it('preserves storage and service import restrictions alongside transport restrictions', () => {
const test = fixture()
try {
const result = test.lint(
"import { get } from './base'\nlocalStorage.getItem('key')\nwindow.localStorage.getItem('key')\nfetch('/one')",
'web/service/feature.ts',
)
expect(result.diagnostics.map((d) => d.labels[0]!.span.line).sort((a, b) => a - b)).toEqual([
1, 2, 3, 4,
])
expect(test.lint("fetch('/one')", 'web/instrumentation-client.ts').diagnostics).toHaveLength(1)
} finally {
test.close()
}
})
it('checks deprecations in all Web scopes while allowing test and script transports', () => {
const test = fixture()
try {
for (const file of [
'web/feature.spec.ts',
'web/feature.test.tsx',
'web/feature.stories.tsx',
'web/__tests__/http.ts',
'web/__mocks__/http.ts',
'web/scripts/check.ts',
'web/test/helper.ts',
'web/vitest.setup.ts',
'web/vitest.browser.setup.ts',
]) {
const source = [
"import ky from 'ky'",
"fetch('/allowed')",
'/** @deprecated Use the replacement. */',
'function legacy() {}',
'legacy()',
].join('\n')
expect(
test.lint(source, file).diagnostics.map((d) => [d.code, d.labels[0]!.span.line]),
).toEqual([['typescript(no-deprecated)', 5]])
}
} finally {
test.close()
}
})
it('rejects additional direct calls beyond the existing suppression count', () => {
const test = fixture()
try {
const source = "import { get } from './service/legacy'\nget('/existing')\nfetch('/existing')"
expect(test.lint(source, undefined, ['--suppress-all']).status).toBe(0)
expect(test.lint(`${source}\nget('/new')\nfetch('/new')`).status).toBe(1)
} finally {
test.close()
}
})
it('documents the accepted Oxlint gap for calls through an already baselined alias', () => {
const test = fixture()
try {
const source = "import { get } from './service/legacy'\nconst saved = get\nsaved('/existing')"
expect(test.lint(source).diagnostics.map((d) => d.labels[0]!.span.line)).toEqual([2])
expect(test.lint(source, undefined, ['--suppress-all']).status).toBe(0)
// Change this expectation and the documentation when Oxlint gains signature propagation.
expect(test.lint(`${source}\nsaved('/new')`).diagnostics).toEqual([])
} finally {
test.close()
}
})
+290 -214
View File
@@ -10,6 +10,242 @@ const webPackageJson = path.resolve(rootDir, 'web/package.json')
const difyUiPackageJson = path.resolve(rootDir, 'packages/dify-ui/package.json')
const enableTailwindCanonicalClasses = process.env.TAILWIND_CANONICAL_CLASSES === 'true'
const handwrittenApiMessage =
'Use generated consoleClient/consoleQuery APIs. New special transports need a scoped lint exception with a concrete reason.'
const rawHttpGlobals = ['fetch', 'XMLHttpRequest'].map((name) => ({
name,
message: handwrittenApiMessage,
}))
const rawHttpProperties = ['window', 'globalThis', 'global', 'self'].flatMap((object) =>
['fetch', 'XMLHttpRequest'].map((property) => ({
object,
property,
message: handwrittenApiMessage,
})),
)
const rawHttpImports = {
group: [
'axios',
'ky',
'ofetch',
'node-fetch',
'cross-fetch',
'isomorphic-fetch',
'undici',
'superagent',
'http',
'https',
'node:http',
'node:https',
].flatMap((name) => [name, `${name}/*`]),
allowTypeImports: true,
message: handwrittenApiMessage,
}
const handwrittenApiScope = {
files: ['web/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
excludeFiles: [
'web/**/__tests__/**',
'web/**/__mocks__/**',
'web/test/**',
'web/scripts/**',
'web/**/*.spec.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/**/*.test.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/**/*.stories.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/**/*.story.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/vitest.setup.ts',
'web/vitest.browser.setup.ts',
],
}
const webRestrictedImports = [
'error',
{
paths: [
{
name: 'next',
message: 'Import Next APIs from the corresponding @/next module instead of next.',
},
{
name: 'ahooks',
importNames: ['useLocalStorageState'],
message: 'Do not use ahooks useLocalStorageState. Use foxact storage hooks instead.',
},
],
patterns: [
{
group: ['next/image'],
message: 'Do not import next/image. Use native img tags instead.',
},
{
group: ['next/font', 'next/font/*'],
message: 'Do not import next/font. Use the project font styles instead.',
},
{
// next/dynamic must be imported directly for compiler-generated preload metadata.
group: [
'next/*',
'!next/dynamic',
'!next/font',
'!next/font/*',
'!next/image',
'!next/image/*',
],
message: 'Import Next APIs from the corresponding @/next/* module instead of next/*.',
},
{
group: ['@base-ui/react', '@base-ui/react/*'],
message:
'Do not import Base UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@floating-ui/*'],
message:
'Do not import Floating UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@/service/base', '@/service/base/*', '**/service/base', '**/service/base/*'],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: ['@/service/fetch', '@/service/fetch/*', '**/service/fetch', '**/service/fetch/*'],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
],
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
const serviceRestrictedImports = [
'error',
{
paths: [
{
name: 'next',
message: 'Import Next APIs from the corresponding @/next module instead of next.',
},
{
name: 'ahooks',
importNames: ['useLocalStorageState'],
message: 'Do not use ahooks useLocalStorageState. Use foxact storage hooks instead.',
},
],
patterns: [
{
group: ['next/image'],
message: 'Do not import next/image. Use native img tags instead.',
},
{
group: ['next/font', 'next/font/*'],
message: 'Do not import next/font. Use the project font styles instead.',
},
{
// next/dynamic must be imported directly for compiler-generated preload metadata.
group: [
'next/*',
'!next/dynamic',
'!next/font',
'!next/font/*',
'!next/image',
'!next/image/*',
],
message: 'Import Next APIs from the corresponding @/next/* module instead of next/*.',
},
{
group: ['@base-ui/react', '@base-ui/react/*'],
message:
'Do not import Base UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@floating-ui/*'],
message:
'Do not import Floating UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@/service/base', '@/service/base/*', '**/service/base', '**/service/base/*'],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: ['@/service/fetch', '@/service/fetch/*', '**/service/fetch', '**/service/fetch/*'],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
{
group: ['./base', './base/*', '../base', '../base/*', '../../base', '../../base/*'],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: ['./fetch', './fetch/*', '../fetch', '../fetch/*', '../../fetch', '../../fetch/*'],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
],
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
const defaultRestrictedGlobals = [
'error',
{
message: 'Use `globalThis` instead.',
name: 'global',
},
{
message: 'Use `globalThis` instead.',
name: 'self',
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
const storageRestrictedGlobals = [
'error',
{
name: 'localStorage',
message:
'Do not use localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
const defaultRestrictedProperties = [
'error',
{
message: 'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.',
property: '__proto__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineGetter__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineSetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupGetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupSetter__',
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
const storageRestrictedProperties = [
'error',
{
object: 'window',
property: 'localStorage',
message:
'Do not use window.localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
{
object: 'globalThis',
property: 'localStorage',
message:
'Do not use globalThis.localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
] satisfies NonNullable<OxlintConfig['rules']>[string]
export const stylingContractRules = {
'shadcn/no-restyle': [
'error',
@@ -387,40 +623,8 @@ export const lintConfig = {
},
],
'no-regex-spaces': 'error',
'no-restricted-globals': [
'error',
{
message: 'Use `globalThis` instead.',
name: 'global',
},
{
message: 'Use `globalThis` instead.',
name: 'self',
},
],
'no-restricted-properties': [
'error',
{
message: 'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.',
property: '__proto__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineGetter__',
},
{
message: 'Use `Object.defineProperty` instead.',
property: '__defineSetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupGetter__',
},
{
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
property: '__lookupSetter__',
},
],
'no-restricted-globals': defaultRestrictedGlobals,
'no-restricted-properties': defaultRestrictedProperties,
'no-self-assign': [
'error',
{
@@ -940,168 +1144,13 @@ export const lintConfig = {
{
files: ['web/**/*.{ts,cts,mts}', 'web/**/*.tsx'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'next',
message: 'Import Next APIs from the corresponding @/next module instead of next.',
},
{
name: 'ahooks',
importNames: ['useLocalStorageState'],
message:
'Do not use ahooks useLocalStorageState. Use foxact storage hooks instead.',
},
],
patterns: [
{
group: ['next/image'],
message: 'Do not import next/image. Use native img tags instead.',
},
{
group: ['next/font', 'next/font/*'],
message: 'Do not import next/font. Use the project font styles instead.',
},
{
// next/dynamic must be imported directly for compiler-generated preload metadata.
group: [
'next/*',
'!next/dynamic',
'!next/font',
'!next/font/*',
'!next/image',
'!next/image/*',
],
message:
'Import Next APIs from the corresponding @/next/* module instead of next/*.',
},
{
group: ['@base-ui/react', '@base-ui/react/*'],
message:
'Do not import Base UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@floating-ui/*'],
message:
'Do not import Floating UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: [
'@/service/base',
'@/service/base/*',
'**/service/base',
'**/service/base/*',
],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: [
'@/service/fetch',
'@/service/fetch/*',
'**/service/fetch',
'**/service/fetch/*',
],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
],
},
],
'no-restricted-imports': webRestrictedImports,
},
},
{
files: ['web/service/**/*.ts', 'web/service/**/*.tsx'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'next',
message: 'Import Next APIs from the corresponding @/next module instead of next.',
},
{
name: 'ahooks',
importNames: ['useLocalStorageState'],
message:
'Do not use ahooks useLocalStorageState. Use foxact storage hooks instead.',
},
],
patterns: [
{
group: ['next/image'],
message: 'Do not import next/image. Use native img tags instead.',
},
{
group: ['next/font', 'next/font/*'],
message: 'Do not import next/font. Use the project font styles instead.',
},
{
// next/dynamic must be imported directly for compiler-generated preload metadata.
group: [
'next/*',
'!next/dynamic',
'!next/font',
'!next/font/*',
'!next/image',
'!next/image/*',
],
message:
'Import Next APIs from the corresponding @/next/* module instead of next/*.',
},
{
group: ['@base-ui/react', '@base-ui/react/*'],
message:
'Do not import Base UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: ['@floating-ui/*'],
message:
'Do not import Floating UI directly in web. Use @langgenius/dify-ui/* primitives instead.',
},
{
group: [
'@/service/base',
'@/service/base/*',
'**/service/base',
'**/service/base/*',
],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: [
'@/service/fetch',
'@/service/fetch/*',
'**/service/fetch',
'**/service/fetch/*',
],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
{
group: ['./base', './base/*', '../base', '../base/*', '../../base', '../../base/*'],
message:
'Do not import legacy service/base fetch helpers. Use generated service clients or feature-specific service modules instead.',
},
{
group: [
'./fetch',
'./fetch/*',
'../fetch',
'../fetch/*',
'../../fetch',
'../../fetch/*',
],
message:
'Do not import low-level service/fetch helpers directly. Use generated service clients or feature-specific service modules instead.',
},
],
},
],
'no-restricted-imports': serviceRestrictedImports,
},
},
{
@@ -1116,29 +1165,8 @@ export const lintConfig = {
'web/instrumentation-client.ts',
],
rules: {
'no-restricted-globals': [
'error',
{
name: 'localStorage',
message:
'Do not use localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
],
'no-restricted-properties': [
'error',
{
object: 'window',
property: 'localStorage',
message:
'Do not use window.localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
{
object: 'globalThis',
property: 'localStorage',
message:
'Do not use globalThis.localStorage directly. Use a foxact storage boundary instead; prefer feature-owned createLocalStorageState for shared storage.',
},
],
'no-restricted-globals': storageRestrictedGlobals,
'no-restricted-properties': storageRestrictedProperties,
},
},
{
@@ -1621,5 +1649,53 @@ export const lintConfig = {
'no-useless-assignment': 'error',
},
},
// Apply after the existing Web overrides and preserve their unrelated restrictions.
{
files: ['web/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
rules: {
'typescript/no-deprecated': 'error',
},
},
{
...handwrittenApiScope,
rules: {
'no-restricted-globals': [...defaultRestrictedGlobals, ...rawHttpGlobals],
'no-restricted-properties': [...defaultRestrictedProperties, ...rawHttpProperties],
'no-restricted-imports': [
'error',
{
...webRestrictedImports[1],
patterns: [...webRestrictedImports[1].patterns, rawHttpImports],
},
],
},
},
{
...handwrittenApiScope,
files: ['web/**/*.{ts,cts,mts}', 'web/**/*.tsx'],
excludeFiles: [
...handwrittenApiScope.excludeFiles,
'web/**/*.bench.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/**/*.benchmark.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
'web/instrumentation-client.ts',
],
rules: {
'no-restricted-globals': [...storageRestrictedGlobals, ...rawHttpGlobals],
'no-restricted-properties': [...storageRestrictedProperties, ...rawHttpProperties],
},
},
{
...handwrittenApiScope,
files: ['web/service/**/*.ts', 'web/service/**/*.tsx'],
rules: {
'no-restricted-imports': [
'error',
{
...serviceRestrictedImports[1],
patterns: [...serviceRestrictedImports[1].patterns, rawHttpImports],
},
],
},
},
],
} satisfies OxlintConfig
+11
View File
@@ -630,6 +630,7 @@ type UploadResponse = {
[key: string]: unknown
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const upload = async (
options: UploadOptions,
isPublicAPI?: boolean,
@@ -704,6 +705,7 @@ export const upload = async (
})
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const ssePost = async (
url: string,
fetchOptions: FetchOptionType,
@@ -878,6 +880,7 @@ export const ssePost = async (
})
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const sseGet = async (
url: string,
fetchOptions: FetchOptionType,
@@ -1064,6 +1067,7 @@ export type GeneratorStreamCallbacks = {
* positional callbacks through that shared, high-blast-radius path isn't worth
* it. This helper reuses the same cookie-auth + CSRF + abort setup but
* only understands the generator's two events: ``plan`` then ``result``.
* @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception.
*/
export const sseGeneratorPost = (
url: string,
@@ -1154,6 +1158,7 @@ export const sseGeneratorPost = (
}
// base request
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const request = async <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
try {
const otherOptionsForBaseFetch = otherOptions || {}
@@ -1262,11 +1267,13 @@ export const get = <T>(url: string, options = {}, otherOptions?: IOtherOptions)
}
// For public API
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const getPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return get<T>(url, options, { ...otherOptions, isPublicAPI: true })
}
// For Marketplace API
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const getMarketplace = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return get<T>(url, options, { ...otherOptions, isMarketplaceAPI: true })
}
@@ -1282,10 +1289,12 @@ export const post = <T>(url: string, options = {}, otherOptions?: IOtherOptions)
}
// For Marketplace API
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const postMarketplace = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return post<T>(url, options, { ...otherOptions, isMarketplaceAPI: true })
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const postPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return post<T>(url, options, { ...otherOptions, isPublicAPI: true })
}
@@ -1300,6 +1309,7 @@ export const del = <T>(url: string, options = {}, otherOptions?: IOtherOptions)
return request<T>(url, Object.assign({}, options, { method: 'DELETE' }), otherOptions)
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const delPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return del<T>(url, options, { ...otherOptions, isPublicAPI: true })
}
@@ -1314,6 +1324,7 @@ export const patch = <T>(url: string, options = {}, otherOptions?: IOtherOptions
return request<T>(url, Object.assign({}, options, { method: 'PATCH' }), otherOptions)
}
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
export const patchPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return patch<T>(url, options, { ...otherOptions, isPublicAPI: true })
}
+2
View File
@@ -110,6 +110,7 @@ export const getBaseOptions = (): RequestInit => ({
redirect: 'follow',
})
/** @deprecated Use generated contract clients for new APIs. Special transports require a scoped lint exception. */
async function base<T>(
url: string,
options: FetchOptionType = {},
@@ -221,6 +222,7 @@ async function base<T>(
* Includes credentials, Authorization (if available), and CSRF header
* so the request is authenticated, matching the headers sent by the
* standard `base()` fetch wrapper.
* @deprecated New keepalive transports require a scoped lint exception.
*/
export function postWithKeepalive(url: string, body: Record<string, unknown>): void {
const headers = new Headers({