@apifuse/provider-sdk 2.2.0-beta.63 → 2.2.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lint.js CHANGED
@@ -1,20 +1,12 @@
1
- import { createRequire } from "node:module";
2
1
  import { SDK_CANONICAL_ERROR_CODE_RETRYABILITY, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "./error-resolution.js";
3
- import { APIFUSE_HANDLE_META_KEY, handleFieldDescription, handleIssuerList, isHandleIssuedBy, isHandleFieldMeta, } from "./handle-meta.js";
2
+ import { APIFUSE_HANDLE_META_KEY, handleFieldDescription, handleFieldNameFor, handleHasDirectionalFieldNames, handleIssuerList, isHandleFieldMeta, isHandleIssuedBy, } from "./handle-meta.js";
4
3
  import { PROVIDER_ERROR_CATALOG_NAMESPACE } from "./i18n/error-messages.js";
5
4
  import { getProviderLocaleSegments, isProviderLocaleKey, } from "./i18n/keys.js";
6
5
  import { lintPublicSchemaFieldNames } from "./public-schema-field-lint.js";
7
6
  import { isStealthOwnedHeaderName, SDK_OWNED_CHROME_HEADER_PREFIX, } from "./runtime/stealth-owned-headers.js";
7
+ import { lintRuntimeBoundary, scriptKindForSourceFile } from "./runtime-boundary-lint.js";
8
8
  import { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_SENSITIVE_META_KEY } from "./schema.js";
9
- const requireModule = createRequire(import.meta.url);
10
- // `typeof import(...)` keeps the type without emitting a static import: the
11
- // typescript package is a CLI-only dependency and src/lint.ts is production
12
- // runtime, which the typescript-import-boundary test enforces.
13
- let typeScriptModule;
14
- function getTypeScript() {
15
- typeScriptModule ??= requireModule("typescript");
16
- return typeScriptModule;
17
- }
9
+ import { getTypeScript } from "./typescript-module.js";
18
10
  const CREDENTIAL_BEARING_AUTH_MODES = [
19
11
  "credentials",
20
12
  "oauth2",
@@ -860,9 +852,9 @@ function isReportedOwnedHeaderName(value) {
860
852
  }
861
853
  return name.startsWith(OWNED_CLIENT_HINT_HEADER_PREFIX);
862
854
  }
863
- function collectBrowserVersionLiteralFindings(source) {
855
+ function collectBrowserVersionLiteralFindings(fileName, source) {
864
856
  const ts = getTypeScript();
865
- const sourceFile = ts.createSourceFile("provider-source.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
857
+ const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, scriptKindForSourceFile(ts, fileName));
866
858
  let findings = [];
867
859
  const seen = new Set();
868
860
  // Owned-header entries only count once the file is known to reach
@@ -980,19 +972,27 @@ function lintBrowserVersionLiterals(provider) {
980
972
  !RECORDED_FIXTURE_SOURCE_FILE_PATTERN.test(filePath));
981
973
  if (sourceFiles.length > 0) {
982
974
  for (const [filePath, source] of sourceFiles) {
983
- sources.push({ field: `sourceFiles.${filePath}`, source });
975
+ sources.push({ field: `sourceFiles.${filePath}`, fileName: filePath, source });
984
976
  }
985
977
  }
986
978
  else {
987
979
  if (provider.authFlowSource)
988
- sources.push({ field: "auth.flow", source: provider.authFlowSource });
980
+ sources.push({
981
+ field: "auth.flow",
982
+ fileName: "auth-flow.ts",
983
+ source: provider.authFlowSource,
984
+ });
989
985
  for (const [operationKey, operation] of Object.entries(provider.operations ?? {})) {
990
986
  const source = getOperationSource(operation);
991
987
  if (source)
992
- sources.push({ field: `operations.${operationKey}.handler`, source });
988
+ sources.push({
989
+ field: `operations.${operationKey}.handler`,
990
+ fileName: `${operationKey}.ts`,
991
+ source,
992
+ });
993
993
  }
994
994
  }
995
- return sources.flatMap(({ field, source }) => collectBrowserVersionLiteralFindings(source).map((finding) => ({
995
+ return sources.flatMap(({ field, fileName, source }) => collectBrowserVersionLiteralFindings(fileName, source).map((finding) => ({
996
996
  rule: "browser-version-literal",
997
997
  level: "error",
998
998
  field,
@@ -1565,6 +1565,7 @@ function readHandleKindDeclarations(value) {
1565
1565
  return (typeof record.name === "string" &&
1566
1566
  (record.type === "cursor" || record.type === "draft") &&
1567
1567
  typeof record.fieldName === "string" &&
1568
+ (record.outputFieldName === undefined || typeof record.outputFieldName === "string") &&
1568
1569
  (record.issuedBy === undefined || isHandleIssuedBy(record.issuedBy)));
1569
1570
  });
1570
1571
  }
@@ -1600,7 +1601,13 @@ function lintHandleDeclarations(provider) {
1600
1601
  for (const { side, fields } of positions) {
1601
1602
  for (const occurrence of fields) {
1602
1603
  const field = `operations.${operationKey}.${occurrence.path}`;
1603
- const { kind, fieldName } = occurrence.meta;
1604
+ const { kind } = occurrence.meta;
1605
+ // A kind may name one handle differently in each direction (the canon's
1606
+ // `cursor` in, `next_cursor` out). The rule is still exact: each side
1607
+ // must use the key declared for THAT side, so the handle's identity
1608
+ // stays readable from the property key and the round trip has exactly
1609
+ // one edge — read `output`, send back under `input`.
1610
+ const expected = handleFieldNameFor(occurrence.meta, side);
1604
1611
  if (!kindByName.has(kind)) {
1605
1612
  diagnostics.push({
1606
1613
  rule: "handle-kind-undeclared",
@@ -1609,12 +1616,16 @@ function lintHandleDeclarations(provider) {
1609
1616
  message: `${providerLabel} operation "${operationKey}" ${side} field "${occurrence.path}" is a handle of kind "${kind}", which is not declared in provider.handle. Add the kind to handle: [...] or remove the field.`,
1610
1617
  });
1611
1618
  }
1612
- if (occurrence.key !== fieldName) {
1619
+ if (occurrence.key !== expected) {
1620
+ const directional = handleHasDirectionalFieldNames(occurrence.meta);
1621
+ const advice = directional
1622
+ ? `Use the kind's ${side} fieldName on every ${side} schema; its ${side === "input" ? "output" : "input"} fieldName is "${handleFieldNameFor(occurrence.meta, side === "input" ? "output" : "input")}".`
1623
+ : "Use the same key in every schema so the LLM sees one name.";
1613
1624
  diagnostics.push({
1614
1625
  rule: "handle-field-name",
1615
1626
  level: "error",
1616
1627
  field,
1617
- message: `${providerLabel} operation "${operationKey}" ${side} declares handle kind "${kind}" under property "${occurrence.key}" but the kind's fieldName is "${fieldName}". Use the same key in every schema so the LLM sees one name.`,
1628
+ message: `${providerLabel} operation "${operationKey}" ${side} declares handle kind "${kind}" under property "${occurrence.key}" but the kind's ${directional ? `${side} ` : ""}fieldName is "${expected}". ${advice}`,
1618
1629
  });
1619
1630
  }
1620
1631
  if (side === "input") {
@@ -1641,7 +1652,7 @@ function lintHandleDeclarations(provider) {
1641
1652
  rule: "handle-field-parity",
1642
1653
  level: "error",
1643
1654
  field,
1644
- message: `${providerLabel} handle kind "${kind.name}" is never issued: no operation output contains its field "${kind.fieldName}". Add ${kind.name}.field() to the issuing operation's output.`,
1655
+ message: `${providerLabel} handle kind "${kind.name}" is never issued: no operation output contains its field "${handleFieldNameFor(kind, "output")}". Add ${kind.name}.field() to the issuing operation's output.`,
1645
1656
  });
1646
1657
  }
1647
1658
  if (!accepted) {
@@ -1649,7 +1660,7 @@ function lintHandleDeclarations(provider) {
1649
1660
  rule: "handle-field-parity",
1650
1661
  level: "error",
1651
1662
  field,
1652
- message: `${providerLabel} handle kind "${kind.name}" is never accepted: no operation input contains its field "${kind.fieldName}". Add ${kind.name}.field() to the consuming operation's input.`,
1663
+ message: `${providerLabel} handle kind "${kind.name}" is never accepted: no operation input contains its field "${handleFieldNameFor(kind, "input")}". Add ${kind.name}.field() to the consuming operation's input.`,
1653
1664
  });
1654
1665
  }
1655
1666
  if (kind.issuedBy === undefined)
@@ -1676,7 +1687,7 @@ function lintHandleDeclarations(provider) {
1676
1687
  rule: "handle-issued-by",
1677
1688
  level: "error",
1678
1689
  field,
1679
- message: `${providerLabel} handle kind "${kind.name}" declares issuedBy "${known[0]}", but that operation's output has no "${kind.fieldName}" handle field.`,
1690
+ message: `${providerLabel} handle kind "${kind.name}" declares issuedBy "${known[0]}", but that operation's output has no "${handleFieldNameFor(kind, "output")}" handle field.`,
1680
1691
  });
1681
1692
  }
1682
1693
  else {
@@ -1686,7 +1697,7 @@ function lintHandleDeclarations(provider) {
1686
1697
  field,
1687
1698
  message: `${providerLabel} handle kind "${kind.name}" declares issuedBy ${known
1688
1699
  .map((operationKey) => `"${operationKey}"`)
1689
- .join(", ")}, but none of those operations' outputs has a "${kind.fieldName}" handle field. At least one listed issuer must output it.`,
1700
+ .join(", ")}, but none of those operations' outputs has a "${handleFieldNameFor(kind, "output")}" handle field. At least one listed issuer must output it.`,
1690
1701
  });
1691
1702
  }
1692
1703
  }
@@ -1711,7 +1722,7 @@ const LEGACY_CHOICE_IDENTIFIERS = new Set([
1711
1722
  * the provider) while code inside `${}` interpolations still does. Falls back
1712
1723
  * to a raw-source regex when TypeScript is not installed.
1713
1724
  */
1714
- function findLegacyChoiceUsage(source) {
1725
+ function findLegacyChoiceUsage(fileName, source) {
1715
1726
  let ts;
1716
1727
  try {
1717
1728
  ts = getTypeScript();
@@ -1719,7 +1730,7 @@ function findLegacyChoiceUsage(source) {
1719
1730
  catch {
1720
1731
  return LEGACY_CHOICE_USAGE_PATTERN.exec(source)?.[0];
1721
1732
  }
1722
- const file = ts.createSourceFile("provider.ts", source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
1733
+ const file = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, false, scriptKindForSourceFile(ts, fileName));
1723
1734
  let found;
1724
1735
  const visit = (node) => {
1725
1736
  if (found)
@@ -1743,19 +1754,23 @@ function findLegacyChoiceUsage(source) {
1743
1754
  function lintLegacyChoiceUsage(provider) {
1744
1755
  const sources = [];
1745
1756
  if (provider.authFlowSource) {
1746
- sources.push({ field: "auth.flow", source: provider.authFlowSource });
1757
+ sources.push({ field: "auth.flow", fileName: "auth-flow.ts", source: provider.authFlowSource });
1747
1758
  }
1748
1759
  for (const [filePath, source] of Object.entries(provider.providerSourceFiles ?? {})) {
1749
- sources.push({ field: `sourceFiles.${filePath}`, source });
1760
+ sources.push({ field: `sourceFiles.${filePath}`, fileName: filePath, source });
1750
1761
  }
1751
1762
  for (const [operationKey, operation] of Object.entries(provider.operations ?? {})) {
1752
1763
  const source = getOperationSource(operation);
1753
1764
  if (source) {
1754
- sources.push({ field: `operations.${operationKey}.handler`, source });
1765
+ sources.push({
1766
+ field: `operations.${operationKey}.handler`,
1767
+ fileName: `${operationKey}.ts`,
1768
+ source,
1769
+ });
1755
1770
  }
1756
1771
  }
1757
- return sources.flatMap(({ field, source }) => {
1758
- const match = findLegacyChoiceUsage(source);
1772
+ return sources.flatMap(({ field, fileName, source }) => {
1773
+ const match = findLegacyChoiceUsage(fileName, source);
1759
1774
  if (!match)
1760
1775
  return [];
1761
1776
  return [
@@ -1872,6 +1887,10 @@ export function lintProviderWithInformation(provider, options = {}) {
1872
1887
  ...lintLegacyChoiceUsage(provider),
1873
1888
  ...lintHandleDeclarations(provider),
1874
1889
  ];
1890
+ // Runtime boundary rules (process.env, node:fs/net/child_process, raw
1891
+ // fetch) also yield information entries for `@apifuse-allow` acknowledgements.
1892
+ const runtimeBoundary = lintRuntimeBoundary(getTypeScript(), provider, getOperationSource);
1893
+ diagnostics.push(...runtimeBoundary.diagnostics);
1875
1894
  if (provider.operations) {
1876
1895
  const authMode = provider.auth?.mode;
1877
1896
  for (const [operationKey, operation] of Object.entries(provider.operations)) {
@@ -1911,7 +1930,7 @@ export function lintProviderWithInformation(provider, options = {}) {
1911
1930
  }
1912
1931
  }
1913
1932
  if (!provider.operations) {
1914
- return applyPinnedWireFieldPaths(provider, diagnostics);
1933
+ return withInformation(applyPinnedWireFieldPaths(provider, diagnostics), runtimeBoundary.information);
1915
1934
  }
1916
1935
  diagnostics.push(...Object.entries(provider.operations).flatMap(([operationKey, operation]) => [
1917
1936
  ...lintOperation({
@@ -1930,5 +1949,10 @@ export function lintProviderWithInformation(provider, options = {}) {
1930
1949
  : `operations.${operationKey}`,
1931
1950
  message: `[${operationKey}] ${diagnostic.message}`,
1932
1951
  }))));
1933
- return applyPinnedWireFieldPaths(provider, diagnostics);
1952
+ return withInformation(applyPinnedWireFieldPaths(provider, diagnostics), runtimeBoundary.information);
1953
+ }
1954
+ function withInformation(result, information) {
1955
+ if (information.length === 0)
1956
+ return result;
1957
+ return { diagnostics: result.diagnostics, information: [...information, ...result.information] };
1934
1958
  }
@@ -11,7 +11,7 @@ export { createProviderEnvironment, createInProcessProviderEngine, ENGINE_OWNED_
11
11
  export type { ProviderCapabilityKey, ProviderEngine, ProviderEngineAttachmentInput, ProviderEngineBindingCandidates, ProviderEngineCapabilitySurface, ProviderEngineMode, ProviderEngineRequest, ProviderEngineResidentSurface, ProviderEngineSession, ProviderEngineTransport, } from "./engine.js";
12
12
  export { getProviderLocalePath, type ProviderErrorMessageParams, providerLocaleKey, qualifyProviderLocaleKey, } from "./i18n/index.js";
13
13
  export * from "./handle.js";
14
- export { APIFUSE_HANDLE_META_KEY, HANDLE_KIND_NAME_PATTERN, handleFieldDescription, isHandleFieldMeta, } from "./handle-meta.js";
14
+ export { APIFUSE_HANDLE_META_KEY, HANDLE_KIND_NAME_PATTERN, handleFieldDescription, handleFieldNameFor, handleHasDirectionalFieldNames, isHandleFieldMeta, } from "./handle-meta.js";
15
15
  export { type CreateHandleContextOptions, type CreateTestHandleContextOptions, createHandleContext, createTestHandleContext, type HandleNormalizationClass, type NormalizedHandle, normalizeHandle, } from "./runtime/handle.js";
16
16
  export { createMemoryProviderRuntimeState, createUnsupportedProviderRuntimeState, } from "./runtime/state.js";
17
17
  export { APIFUSE_CONTENT_PROVENANCE_META_KEY, APIFUSE_CONTENT_TRUST_META_KEY, APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, publicField, redactPayload, type SensitiveFieldKind, type SensitiveFieldOptions, type SensitivePath, sensitive, untrustedContent, z, } from "./schema.js";
package/dist/provider.js CHANGED
@@ -6,7 +6,7 @@ export { AuthError, HttpRedirectError, isProviderError, isSessionExpiredError, i
6
6
  export { createProviderEnvironment, createInProcessProviderEngine, ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, ENGINE_OWNED_RESOLVER_CREDENTIAL_ENV_NAMES, ENGINE_OWNED_RUNTIME_ENV_NAMES, ENGINE_OWNED_TELEMETRY_ENV_NAMES, isEngineOwnedEnvName, isEngineOwnedProxyCredentialName, isEngineOwnedResolverCredentialName, isEngineOwnedRuntimeEnvName, isEngineOwnedTelemetryEnvName, PROVIDER_CAPABILITY_KEYS, PROVIDER_ENGINE_MODE_ENV, PROVIDER_ENGINE_PROTOCOL_VERSION, readEngineProxyCredentials, } from "./engine.js";
7
7
  export { getProviderLocalePath, providerLocaleKey, qualifyProviderLocaleKey, } from "./i18n/index.js";
8
8
  export * from "./handle.js";
9
- export { APIFUSE_HANDLE_META_KEY, HANDLE_KIND_NAME_PATTERN, handleFieldDescription, isHandleFieldMeta, } from "./handle-meta.js";
9
+ export { APIFUSE_HANDLE_META_KEY, HANDLE_KIND_NAME_PATTERN, handleFieldDescription, handleFieldNameFor, handleHasDirectionalFieldNames, isHandleFieldMeta, } from "./handle-meta.js";
10
10
  export { createHandleContext, createTestHandleContext, normalizeHandle, } from "./runtime/handle.js";
11
11
  export { createMemoryProviderRuntimeState, createUnsupportedProviderRuntimeState, } from "./runtime/state.js";
12
12
  export { APIFUSE_CONTENT_PROVENANCE_META_KEY, APIFUSE_CONTENT_TRUST_META_KEY, APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, publicField, redactPayload, sensitive, untrustedContent, z, } from "./schema.js";
@@ -11,7 +11,7 @@
11
11
  import { randomInt, randomUUID } from "node:crypto";
12
12
  import { isProviderError } from "../errors.js";
13
13
  import { HandleError, handleRecoverySentence, isDraftKind, } from "../handle.js";
14
- import { formatHandleIssuers } from "../handle-meta.js";
14
+ import { formatHandleIssuers, handleHasDirectionalFieldNames } from "../handle-meta.js";
15
15
  import { BOUND_HANDLE_WORD_COUNT, findHandleWordWithinDistance, HIGH_PUBLIC_HANDLE_WORD_COUNT, ISSUABLE_HANDLE_WORDS, PUBLIC_HANDLE_WORD_COUNT, } from "./handle-wordlist.js";
16
16
  import { createMemoryProviderRuntimeState } from "./state.js";
17
17
  const RECORD_VERSION = 1;
@@ -153,6 +153,14 @@ export function formatNormalizationClasses(classes) {
153
153
  // Errors (spec §5)
154
154
  // ---------------------------------------------------------------------------
155
155
  function publicCollapsedMessage(kind) {
156
+ // "a new one" is unambiguous only while the kind has one name. When it is
157
+ // issued under `next_cursor` and accepted under `cursor`, the caller has to
158
+ // be told which field to read, so use the full recovery sentence — it names
159
+ // both keys and is identical for invalid / expired / not-found, which is
160
+ // what keeps the public collapse indistinguishable.
161
+ if (handleHasDirectionalFieldNames(kind)) {
162
+ return `\`${kind.fieldName}\` is not valid or has expired. ${handleRecoverySentence(kind)}`;
163
+ }
156
164
  const issuers = formatHandleIssuers(kind.issuedBy);
157
165
  const again = issuers ? `Call ${issuers} again to get a new one.` : "Request a new one.";
158
166
  return `\`${kind.fieldName}\` is not valid or has expired. ${again}`;
@@ -42,6 +42,10 @@ function createErrorOcrClient(options) {
42
42
  });
43
43
  };
44
44
  return tagCapability({
45
+ // Own data property: `bindOcrTelemetry` re-spreads the context, so a
46
+ // getter or prototype member would silently vanish before reaching
47
+ // provider code.
48
+ available: false,
45
49
  async recognize() {
46
50
  return unavailable();
47
51
  },
@@ -274,6 +278,7 @@ function toOcrTransportError(error) {
274
278
  }
275
279
  function createOcrClient(model, recognize) {
276
280
  return {
281
+ available: true,
277
282
  recognize,
278
283
  async extractCaptchaText(image, options = {}) {
279
284
  const result = await recognize({ image, hint: "captcha" });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Runtime boundary lint: provider runtime source must reach the outside world
3
+ * through the SDK context (`ctx.http`, `ctx.stealth`, `ctx.env`,
4
+ * `ctx.credential`, `ctx.browser`), not through ambient Node/Bun globals.
5
+ *
6
+ * Three rules, all reported at `warn` level in this first release so the
7
+ * fleet can migrate before the level is raised:
8
+ *
9
+ * - `process-env-direct-read`: `process.env.X`, `process.env["X"]`, a bare
10
+ * `process.env` object use, and the `Bun.env` equivalents. Runtime bootstrap
11
+ * keys (`APIFUSE__RUNTIME__*`) are exempt everywhere because `serve()`
12
+ * callers read the port and pod identity before a context exists.
13
+ * - `node-runtime-module-import`: value imports of `fs`, `fs/promises`, `net`,
14
+ * `tls`, `dgram`, `http`, `https`, `http2`, `child_process` (with or without
15
+ * the `node:` prefix) via import/export declarations, `require()`, or dynamic
16
+ * `import()`, plus the Bun equivalents `Bun.spawn`, `Bun.spawnSync`, `Bun.$`,
17
+ * `Bun.file`, `Bun.write`. Type-only imports are not runtime reach and are
18
+ * ignored.
19
+ * - `direct-fetch-call`: a call to the global `fetch` (bare identifier, or
20
+ * `globalThis`/`window`/`self`/`global` member). A bare `fetch` call whose
21
+ * enclosing scope declares a `fetch` binding (parameter, variable, import,
22
+ * function, catch clause) is calling that binding, not the global, and is
23
+ * left alone; the check is lexical, so a wrapper that takes `fetch` as a
24
+ * parameter does not hide a global `fetch()` elsewhere in the same file.
25
+ * `ctx.stealth.fetch()` is a member call and never matches.
26
+ *
27
+ * Files are parsed with the script kind their extension implies (`.ts` as
28
+ * TypeScript, `.tsx` as TSX, …). Parsing everything as TSX would make a
29
+ * generic arrow (`<T>(value: T) => value`) or an angle-bracket assertion in an
30
+ * ordinary `.ts` file read as JSX, and everything after it would silently drop
31
+ * out of the AST.
32
+ *
33
+ * Scope is the provider *runtime* source: JavaScript/TypeScript files under
34
+ * `providerSourceFiles` minus tests, recorded fixtures, the root bootstrap
35
+ * entrypoints (`dev.ts`, `start.ts`, `deploy.ts`) and the operator tooling
36
+ * directories (`scripts/`, `tools/`, `bin/`). Tooling legitimately reads the
37
+ * ambient environment and the filesystem when a human runs it; the rule is
38
+ * about code the pod executes on behalf of a request.
39
+ *
40
+ * A deliberate exception is acknowledged with the same escape hatch the submit
41
+ * check uses: `// @apifuse-allow <rule>: <reason>` on the finding line or the
42
+ * line directly above it. Acknowledged findings are reported as information so
43
+ * `apifuse check` still lists them.
44
+ */
45
+ import type { LintDiagnostic, ProviderLintInformation, ProviderLintResult } from "./lint.js";
46
+ import { type TypeScriptCompilerModuleLike, type TypeScriptModule } from "./typescript-module.js";
47
+ export declare const PROCESS_ENV_DIRECT_READ_RULE = "process-env-direct-read";
48
+ export declare const NODE_RUNTIME_MODULE_IMPORT_RULE = "node-runtime-module-import";
49
+ export declare const DIRECT_FETCH_CALL_RULE = "direct-fetch-call";
50
+ export declare const RUNTIME_BOUNDARY_RULES: readonly ["process-env-direct-read", "node-runtime-module-import", "direct-fetch-call"];
51
+ export type RuntimeBoundaryRule = (typeof RUNTIME_BOUNDARY_RULES)[number];
52
+ /**
53
+ * Environment names a provider may read from `process.env` directly. These
54
+ * are consumed before `serve()` builds a context (port, pod identity), so
55
+ * `ctx.env` cannot carry them. The whole `APIFUSE__RUNTIME__` family is the
56
+ * bootstrap contract; nothing else is exempt.
57
+ */
58
+ export declare const BOOTSTRAP_ENV_NAME_PREFIX = "APIFUSE__RUNTIME__";
59
+ export declare function isBootstrapEnvName(name: string): boolean;
60
+ type RuntimeBoundaryFinding = {
61
+ rule: RuntimeBoundaryRule;
62
+ /** 1-based line. */
63
+ line: number;
64
+ /** What was reached: the env name, module specifier, or call text. */
65
+ subject: string;
66
+ };
67
+ type RuntimeBoundaryFileReport = {
68
+ findings: RuntimeBoundaryFinding[];
69
+ acknowledged: Array<RuntimeBoundaryFinding & {
70
+ reason: string;
71
+ }>;
72
+ };
73
+ /** True for the files the runtime boundary rules look at. */
74
+ export declare function isRuntimeBoundarySourceFile(relativePath: string): boolean;
75
+ /**
76
+ * Script kind for a provider source file, from its extension. `.tsx`/`.jsx`
77
+ * enable JSX; everything else (`.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`,
78
+ * and the synthetic names used for in-memory sources) does not, so a generic
79
+ * arrow or `<T>value` assertion in a `.ts` file parses as TypeScript instead
80
+ * of swallowing the rest of the file as unterminated JSX.
81
+ */
82
+ export declare function scriptKindForSourceFile(ts: TypeScriptModule, fileName: string): import("typescript").ScriptKind;
83
+ export declare function analyzeRuntimeBoundary(ts: TypeScriptModule, fileName: string, source: string): RuntimeBoundaryFileReport;
84
+ export type RuntimeBoundaryLintInput = {
85
+ authFlowSource?: string;
86
+ providerSourceFiles?: Record<string, string>;
87
+ operations?: Record<string, {
88
+ handler?: unknown;
89
+ source?: string;
90
+ }>;
91
+ };
92
+ /**
93
+ * Lint the provider runtime boundary. Returns warn-level diagnostics (one per
94
+ * file and rule, listing every line) and information entries for findings
95
+ * acknowledged with `@apifuse-allow`.
96
+ */
97
+ export declare function lintRuntimeBoundary(ts: TypeScriptModule, provider: RuntimeBoundaryLintInput, readOperationSource: (operation: {
98
+ handler?: unknown;
99
+ source?: string;
100
+ }) => string): {
101
+ diagnostics: LintDiagnostic[];
102
+ information: ProviderLintInformation[];
103
+ };
104
+ export type LintRuntimeBoundarySourcesOptions = {
105
+ /**
106
+ * A loaded TypeScript compiler API module to parse with (`typescript` 6.x
107
+ * or `@typescript/typescript6`). When omitted the SDK resolves one from its
108
+ * own install location, trying `typescript` first and falling back to
109
+ * `@typescript/typescript6` when `typescript` is the 7.x native shell
110
+ * without `createSourceFile`. A caller whose dependency tree pins its own
111
+ * compiler module (the APIFuse monorepo contract check) passes it here so
112
+ * the parse does not depend on what hoists next to the SDK.
113
+ */
114
+ typescript?: TypeScriptCompilerModuleLike;
115
+ };
116
+ /**
117
+ * Lint provider runtime source files for the runtime boundary rules without a
118
+ * provider definition: `files` maps provider-root-relative paths to source
119
+ * text (the same shape `apifuse check` collects as `providerSourceFiles`).
120
+ * Out-of-scope paths (tests, recorded fixtures, `.d.ts`, the root
121
+ * `dev.ts`/`start.ts`/`deploy.ts`, `scripts/`, `tools/`, `bin/`) are skipped
122
+ * with `isRuntimeBoundarySourceFile`. Diagnostics carry
123
+ * `field: "sourceFiles.<path>"`; `@apifuse-allow` acknowledgements come back
124
+ * as `information`.
125
+ *
126
+ * Parses with `options.typescript` when given, otherwise with the compiler
127
+ * module resolved from the installed SDK (see `getTypeScript`). This is the
128
+ * entry point the platform contract check uses to apply the rules with the
129
+ * monorepo's SDK pin instead of each provider's own pin.
130
+ */
131
+ export declare function lintRuntimeBoundarySources(files: Record<string, string>, options?: LintRuntimeBoundarySourcesOptions): ProviderLintResult;
132
+ export {};