@apifuse/provider-sdk 2.2.0-beta.64 → 2.2.0-beta.66
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/AUTHORING.md +12 -4
- package/CHANGELOG.md +8 -0
- package/bin/apifuse-check.ts +72 -3
- package/dist/lint.js +526 -1
- package/dist/runtime-boundary-lint.d.ts +8 -3
- package/dist/runtime-boundary-lint.js +9 -5
- package/package.json +1 -1
- package/src/lint.ts +587 -1
- package/src/runtime-boundary-lint.ts +9 -5
package/AUTHORING.md
CHANGED
|
@@ -900,8 +900,13 @@ Provider runtime code reaches the outside world through the context only:
|
|
|
900
900
|
`ctx.env.get()` / `ctx.credential` for configuration and secrets, `ctx.files` /
|
|
901
901
|
`ctx.cache` / `ctx.state` for data that must outlive a request. Ambient Node
|
|
902
902
|
and Bun globals bypass `allowedHosts`, proxy policy, retries, redaction, the
|
|
903
|
-
secret presence gate, and telemetry. `apifuse check` reports them
|
|
904
|
-
|
|
903
|
+
secret presence gate, and telemetry. `apifuse check` reports them at **error**
|
|
904
|
+
level — a finding fails `apifuse check` and `apifuse submit-check`, in the
|
|
905
|
+
official and the bounty `standalone` mode alike (the level was `warn` for one
|
|
906
|
+
release as a migration window; the window closed once the fleet's request-path
|
|
907
|
+
hits were fixed or acknowledged — a provider that still carries one sees
|
|
908
|
+
`apifuse check` fail on its next SDK bump and fixes or acknowledges it in that
|
|
909
|
+
same PR):
|
|
905
910
|
|
|
906
911
|
- `process-env-direct-read` — `process.env.X`, `process.env["X"]`, a bare
|
|
907
912
|
`process.env` object use, `Bun.env`. The `APIFUSE__RUNTIME__*` bootstrap
|
|
@@ -931,11 +936,14 @@ Scope is runtime source only: tests, recorded fixtures, `.d.ts`, the root
|
|
|
931
936
|
`dev.ts` / `start.ts` / `deploy.ts` entrypoints, and the `scripts/`, `tools/`,
|
|
932
937
|
`bin/` directories at the provider root are not scanned. Keep fixture
|
|
933
938
|
recorders, smoke scripts, and other operator tooling under `scripts/`, and do
|
|
934
|
-
not import from there in request-path modules.
|
|
939
|
+
not import from there in request-path modules. `apifuse check` also leaves out
|
|
940
|
+
any directory that is itself a git checkout (a linked worktree such as
|
|
941
|
+
`.worktree/<branch>/`, a nested clone, an initialised submodule): its files are
|
|
942
|
+
another revision, checked in that checkout, not this provider's runtime source.
|
|
935
943
|
|
|
936
944
|
Acknowledge a deliberate exception on the finding line or the comment-only
|
|
937
945
|
line above it with `// @apifuse-allow <rule>: <reason>`; the finding then
|
|
938
|
-
appears as an `INFO` audit line instead of
|
|
946
|
+
appears as an `INFO` audit line instead of an error, and the check passes.
|
|
939
947
|
|
|
940
948
|
```ts
|
|
941
949
|
// Before: request-path module reads the ambient environment and the disk
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @apifuse/provider-sdk Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.0-beta.66
|
|
4
|
+
|
|
5
|
+
- Release candidate for main commit e7f93a6c71e3bca3a680861c0968e22dbe5d1d40.
|
|
6
|
+
|
|
7
|
+
## 2.2.0-beta.65
|
|
8
|
+
|
|
9
|
+
- Release candidate for main commit 4731ac76145caf85a6c340ad0c446371bd24b052.
|
|
10
|
+
|
|
3
11
|
## 2.2.0-beta.64
|
|
4
12
|
|
|
5
13
|
- Release candidate for main commit 0505a938340d08db3ea3e2302cab5f6b5dd2b4e5.
|
package/bin/apifuse-check.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
|
|
7
7
|
import { z } from "zod";
|
|
@@ -226,6 +226,21 @@ function isScannableProviderSourceFile(relativePath: string): boolean {
|
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
/**
|
|
230
|
+
* A directory under the provider root that is itself a git checkout — a linked
|
|
231
|
+
* worktree (`.worktree/<branch>/`, whose `.git` is a file pointing at the main
|
|
232
|
+
* repository), a nested clone (`.git` directory), or an initialised submodule.
|
|
233
|
+
* Its files are another revision of some repository, not this provider's
|
|
234
|
+
* runtime source: an older copy of `upstream/` or `scripts/` there would be
|
|
235
|
+
* linted as if it were request-path code at the root (the tooling-directory
|
|
236
|
+
* exemption is anchored at the provider root), and its `apifuse check` is run
|
|
237
|
+
* in that checkout, not here. Git refuses to track a path named `.git`, so the
|
|
238
|
+
* marker cannot be planted in a committed tree to hide a file from the scan.
|
|
239
|
+
*/
|
|
240
|
+
function isNestedCheckout(directory: string): boolean {
|
|
241
|
+
return existsSync(join(directory, ".git"));
|
|
242
|
+
}
|
|
243
|
+
|
|
229
244
|
function collectProviderSourceFiles(providerRoot: string): Record<string, string> {
|
|
230
245
|
const sources: Record<string, string> = {};
|
|
231
246
|
// `.agents`/`.apifuse` are deliberately not skipped: managed content there
|
|
@@ -236,7 +251,7 @@ function collectProviderSourceFiles(providerRoot: string): Record<string, string
|
|
|
236
251
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
237
252
|
const path = resolve(directory, entry.name);
|
|
238
253
|
if (entry.isDirectory()) {
|
|
239
|
-
if (!skipDirectories.has(entry.name)) {
|
|
254
|
+
if (!skipDirectories.has(entry.name) && !isNestedCheckout(path)) {
|
|
240
255
|
visit(path);
|
|
241
256
|
}
|
|
242
257
|
continue;
|
|
@@ -573,12 +588,56 @@ function checkPackageJson(packageJsonPath: string): CheckResult {
|
|
|
573
588
|
|
|
574
589
|
export const PROVIDER_JSON_CHECK_MESSAGE = "provider.json exists with a valid declaration";
|
|
575
590
|
|
|
591
|
+
/**
|
|
592
|
+
* Markets a provider may declare as its primary, and the locales the platform
|
|
593
|
+
* catalogs can carry.
|
|
594
|
+
*
|
|
595
|
+
* These mirror `scripts/provider-deploy/provider-locale-coverage.mjs` in the
|
|
596
|
+
* APIFuse monorepo, which is the policy's source of truth. The duplication is
|
|
597
|
+
* deliberate and bounded: the monorepo cannot be imported from a provider
|
|
598
|
+
* checkout, and a *closed* mirror is the point — it keeps a typo
|
|
599
|
+
* (`primaryMarket: "jp-JP"`) a local failure instead of one that only surfaces
|
|
600
|
+
* in the monorepo's contract validator days later.
|
|
601
|
+
*/
|
|
602
|
+
const PROVIDER_PRIMARY_MARKETS = ["jp", "kr", "global"] as const;
|
|
603
|
+
const PROVIDER_COVERAGE_LOCALES = ["en", "ko", "ja"] as const;
|
|
604
|
+
const PROVIDER_BASELINE_LOCALE = "en";
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Declared locale coverage, optional at schemaVersion 1.
|
|
608
|
+
*
|
|
609
|
+
* `apifuse provider declare-locale-coverage` writes this block, and the
|
|
610
|
+
* monorepo declaration schema has accepted it since the rollout landed. This
|
|
611
|
+
* check used to close `provider.json` over four keys, which made the only
|
|
612
|
+
* documented way to clear the monorepo's `LOCALE_COVERAGE_UNDECLARED` finding
|
|
613
|
+
* turn a green `bun run check` red — the declaration was unfixable from either
|
|
614
|
+
* side. The fix is to mirror the monorepo schema rather than to stop
|
|
615
|
+
* validating: the block stays closed over its own two keys so a misspelled
|
|
616
|
+
* field is still caught here, at authoring time.
|
|
617
|
+
*/
|
|
618
|
+
const providerI18nDeclarationSchema = z
|
|
619
|
+
.object({
|
|
620
|
+
primaryMarket: z.enum(PROVIDER_PRIMARY_MARKETS),
|
|
621
|
+
locales: z
|
|
622
|
+
.array(z.enum(PROVIDER_COVERAGE_LOCALES))
|
|
623
|
+
.nonempty()
|
|
624
|
+
.refine((locales) => new Set(locales).size === locales.length, {
|
|
625
|
+
message: "i18n.locales must not repeat a locale",
|
|
626
|
+
})
|
|
627
|
+
.refine((locales) => locales.includes(PROVIDER_BASELINE_LOCALE), {
|
|
628
|
+
message: `i18n.locales must include "${PROVIDER_BASELINE_LOCALE}": it is the fallback every other locale resolves through`,
|
|
629
|
+
})
|
|
630
|
+
.optional(),
|
|
631
|
+
})
|
|
632
|
+
.strict();
|
|
633
|
+
|
|
576
634
|
const providerDeclarationSchema = z
|
|
577
635
|
.object({
|
|
578
636
|
schemaVersion: z.literal(1),
|
|
579
637
|
providerId: z.string(),
|
|
580
638
|
owner: z.string(),
|
|
581
639
|
lifecycle: z.enum(["draft", "ready", "live", "retired"]),
|
|
640
|
+
i18n: providerI18nDeclarationSchema.optional(),
|
|
582
641
|
})
|
|
583
642
|
.strict();
|
|
584
643
|
|
|
@@ -609,7 +668,17 @@ function checkProviderJson(providerJsonPath: string, packageJsonPath: string): C
|
|
|
609
668
|
return {
|
|
610
669
|
message: PROVIDER_JSON_CHECK_MESSAGE,
|
|
611
670
|
passed: true,
|
|
612
|
-
details: [
|
|
671
|
+
details: [
|
|
672
|
+
`providerId: ${declaration.providerId}`,
|
|
673
|
+
`lifecycle: ${declaration.lifecycle}`,
|
|
674
|
+
...(declaration.i18n
|
|
675
|
+
? [
|
|
676
|
+
`locale coverage: primaryMarket ${declaration.i18n.primaryMarket}${
|
|
677
|
+
declaration.i18n.locales ? ` (locales: ${declaration.i18n.locales.join(", ")})` : ""
|
|
678
|
+
}`,
|
|
679
|
+
]
|
|
680
|
+
: []),
|
|
681
|
+
],
|
|
613
682
|
};
|
|
614
683
|
} catch (error) {
|
|
615
684
|
return {
|