@ggui-ai/registry-core 0.8.0 → 0.9.0

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.
@@ -174,6 +174,13 @@ function parseCursor(cursor) {
174
174
  return n;
175
175
  }
176
176
  function rowMatchesFilter(row, q) {
177
+ // #474 — visibility gating lives INSIDE every scanArtifacts impl
178
+ // (the ruled canonical location); the search op's post-filter stays
179
+ // as defense-in-depth. Without this, private rows would consume
180
+ // page slots on self-host impls even though the op filters them out
181
+ // before they ever reach the wire.
182
+ if (row.visibility !== 'public')
183
+ return false;
177
184
  if (q.kind !== undefined && row.kind !== q.kind)
178
185
  return false;
179
186
  if (q.hook !== undefined && row.hook !== q.hook)
@@ -60,6 +60,14 @@
60
60
  * together they require one entry with exactly that (server, tool)
61
61
  * pair. Case-sensitive exact, AND-composed with every other
62
62
  * dimension.
63
+ * - {@link scanArtifacts} MUST exclude non-public rows
64
+ * (`visibility !== 'public'`) — visibility gating lives INSIDE every
65
+ * impl, not only at the caller layer (#259 delta-2 final-review
66
+ * ruling, #474). {@link searchArtifacts}'s post-fetch visibility
67
+ * filter is defense-in-depth ONLY; it must never be the sole gate,
68
+ * because a private row that reaches the storage-layer page would
69
+ * consume a `limit` slot before the op ever gets to discard it —
70
+ * an observable underfill on self-host impls.
63
71
  *
64
72
  * **Failure mode:**
65
73
  * - Transport-level failures (store throttling, disk full) throw.
@@ -73,8 +81,11 @@
73
81
  * `putArtifactVersionIfAbsent` rejects on collision, missing returns
74
82
  * null, `listAuthorKeys` returns only keys for the queried subject,
75
83
  * `deleteAuthorKey` true/false idempotency + per-subject isolation,
76
- * and `order: 'recent'` newest-first ordering (single page + across
77
- * the cursor chain).
84
+ * `order: 'recent'` newest-first ordering (single page + across the
85
+ * cursor chain), the `tool` / `server` filter dimensions (tool-only,
86
+ * server-only with bare-entry exclusion, and the exact-pair
87
+ * cross-product case), and non-public rows never appearing in
88
+ * `scanArtifacts` results OR consuming a `limit` page slot.
78
89
  */
79
90
  import type { ArtifactScanFilter, ArtifactVersionRow, ArtifactsMetadataRow, AuthorKeyRow, CompiledBlobRow, ScopeOwnerRow } from '../types.js';
80
91
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AACH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAQ3C;AAED;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACtD,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAE9B,MAAM,WAAW,eAAe;IAE9B,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC9E,mBAAmB,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QACjD,QAAQ,CAAC,IAAI,EAAE,SAAS,oBAAoB,EAAE,CAAC;QAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC,CAAC;IAGH,kBAAkB,CAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IACtC;;;;;;;;;;;;;;;;;OAiBG;IACH,oBAAoB,CAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,0BAA0B,CACxB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IACnE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGxE;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,oBAAoB,CAClB,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,eAAe,GACvB,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAA;KAAE,GACxC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAC1C,CAAC;IAGF;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC5D;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAC3E;;;;;;;;;;;;;;;;;;;OAmBG;IACH,gBAAgB,CACd,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAG9C,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC3E;;;;;;OAMG;IACH,YAAY,CACV,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;IAClE;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/ops/compile.ts"],"names":[],"mappings":"AAoDA,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1E,CAAC,CAAC;CACJ;AAED,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CA4CvE"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/ops/compile.ts"],"names":[],"mappings":"AAwEA,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1E,CAAC,CAAC;CACJ;AAED,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CA6CvE"}
@@ -46,9 +46,26 @@
46
46
  * load-bearing one (its OUTPUT is what's stored), but the two are
47
47
  * deliberately separate so a `POST /conformance/check` dry-run
48
48
  * remains cheap (no bundle resolution).
49
+ *
50
+ * **Lazy dependency load.** `esbuild` ships its own platform-specific
51
+ * native binary; loading it costs real init time and can fail outright
52
+ * on a host with no matching binary for the running platform. Callers
53
+ * that never reach `compileBlueprint` (most registry operations don't
54
+ * touch blueprint compilation) shouldn't pay that cost just for
55
+ * importing this module. `esbuild` resolves as an ordinary CommonJS
56
+ * package, so a `require()` obtained via `createRequire` defers the
57
+ * load to the first actual call, memoized at module scope so a
58
+ * long-lived process only pays it once — with NO change to
59
+ * {@link compileBlueprint}'s synchronous signature.
49
60
  */
50
- import * as esbuild from 'esbuild';
61
+ import { createRequire } from 'node:module';
51
62
  import { createHash } from 'node:crypto';
63
+ const nodeRequire = createRequire(import.meta.url);
64
+ let esbuildModule;
65
+ /** Lazy, memoized `esbuild` accessor — see the file-level doc above. */
66
+ function loadEsbuild() {
67
+ return (esbuildModule ??= nodeRequire('esbuild'));
68
+ }
52
69
  /**
53
70
  * Compile a blueprint's TSX `source` into canonical compiled JS bytes.
54
71
  *
@@ -63,6 +80,7 @@ import { createHash } from 'node:crypto';
63
80
  */
64
81
  export function compileBlueprint(source) {
65
82
  try {
83
+ const esbuild = loadEsbuild();
66
84
  // esbuild.buildSync would require a virtual-fs layer to handle
67
85
  // imports; we use transformSync which compiles a single text input
68
86
  // and emits a single text output. Tree-shaking is OFF and externals
@@ -65,7 +65,7 @@
65
65
  * registry-core to keep this layer free of DOM-emulation deps.
66
66
  */
67
67
  import { type ArtifactManifest } from '@ggui-ai/artifact-manifest';
68
- import { type StaticExport, type StaticImport } from 'oxc-parser';
68
+ import type { StaticExport, StaticImport } from 'oxc-parser';
69
69
  /**
70
70
  * Body shape of the conformance request. `bundle` is the UTF-8 text of
71
71
  * the compiled gadget entry; required for `kind: "gadget"` manifests,
@@ -116,16 +116,19 @@ export interface ConformanceResponseBody {
116
116
  */
117
117
  export declare const MAX_BLUEPRINT_SOURCE_BYTES: number;
118
118
  /**
119
- * Run the conformance check synchronously. Pure function: no
120
- * network, no env lookups. Returns the locked response body shape.
121
- * Every registry transport and the publish op call this.
119
+ * Run the conformance check. Pure beyond the (memoized, in-process)
120
+ * `oxc-parser` load: no network, no env lookups. Resolves to the
121
+ * locked response body shape. Every registry transport and the
122
+ * publish op call this — all are already async, so awaiting here adds
123
+ * no restructuring on the caller side; see `loadOxcParser`'s doc for
124
+ * why this can't stay synchronous.
122
125
  *
123
126
  * Static gates only — the optional runtime probe lives behind a
124
127
  * {@link BlueprintProbeRunner} seam so registry-core stays free of
125
128
  * DOM-emulation deps (happy-dom + react-dom/server land in the caller
126
129
  * that wants the probe).
127
130
  */
128
- export declare function checkConformance(payload: ConformanceRequestPayload): ConformanceResponseBody;
131
+ export declare function checkConformance(payload: ConformanceRequestPayload): Promise<ConformanceResponseBody>;
129
132
  /**
130
133
  * Runtime-probe seam. Implemented by callers that opt into the
131
134
  * sandboxed render check. registry-core does not provide a default
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../src/ops/conformance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,wBAAwB,GACxB,4BAA4B,GAC5B,yBAAyB,GACzB,6BAA6B,GAC7B,kCAAkC,GAClC,8BAA8B,GAC9B,gCAAgC,CAAC;AAErC,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC9C;AAkBD;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,yBAAyB,GACjC,uBAAuB,CAgBzB;AAmbD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,KAAK,CACH,QAAQ,EAAE,OAAO,CAAC,gBAAgB,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,GACzD,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../src/ops/conformance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAEV,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AA4BpB;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,wBAAwB,GACxB,4BAA4B,GAC5B,yBAAyB,GACzB,6BAA6B,GAC7B,kCAAkC,GAClC,8BAA8B,GAC9B,gCAAgC,CAAC;AAErC,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC9C;AAkBD;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,uBAAuB,CAAC,CAgBlC;AAqbD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,KAAK,CACH,QAAQ,EAAE,OAAO,CAAC,gBAAgB,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,GACzD,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
@@ -65,7 +65,30 @@
65
65
  * registry-core to keep this layer free of DOM-emulation deps.
66
66
  */
67
67
  import { safeParseArtifactManifest, } from '@ggui-ai/artifact-manifest';
68
- import { parseSync, } from 'oxc-parser';
68
+ import { memoizedRetryingImport } from '../utils/lazy-import.js';
69
+ /**
70
+ * Lazy, memoized `oxc-parser` accessor. `oxc-parser` ships its own
71
+ * platform-specific native binding; loading it costs real init time
72
+ * and can fail outright on a host with no matching binding for the
73
+ * running platform. Callers that never reach the parse gates (most
74
+ * registry operations don't touch bundle/source conformance) shouldn't
75
+ * pay that cost just for importing this module — or the barrel that
76
+ * re-exports it.
77
+ *
78
+ * `oxc-parser` ships ESM-only (no CommonJS entry point), so a dynamic
79
+ * `import()` is the only way to defer the load — unlike `esbuild` in
80
+ * `compile.ts`, there's no synchronous `require()` path available.
81
+ * That's what pushes {@link checkConformance} (and everything that
82
+ * calls it) from a plain synchronous function to one returning a
83
+ * `Promise`. {@link memoizedRetryingImport} memoizes the promise at
84
+ * module scope — concurrent first callers share one in-flight load,
85
+ * and a long-lived process pays the import cost at most once per
86
+ * successful load — while clearing the cache on rejection, so a
87
+ * failed load doesn't poison every later call for the rest of the
88
+ * process (see that helper's doc for why a bare `??=` isn't safe
89
+ * here).
90
+ */
91
+ const loadOxcParser = memoizedRetryingImport(() => import('oxc-parser'));
69
92
  /**
70
93
  * Always-permitted import sources for gadget bundles. Mirrors the
71
94
  * runtime CSP the iframe enforces — these are the only modules the
@@ -91,16 +114,19 @@ const ALWAYS_ALLOWED_IMPORTS = new Set([
91
114
  */
92
115
  export const MAX_BLUEPRINT_SOURCE_BYTES = 5 * 1024 * 1024;
93
116
  /**
94
- * Run the conformance check synchronously. Pure function: no
95
- * network, no env lookups. Returns the locked response body shape.
96
- * Every registry transport and the publish op call this.
117
+ * Run the conformance check. Pure beyond the (memoized, in-process)
118
+ * `oxc-parser` load: no network, no env lookups. Resolves to the
119
+ * locked response body shape. Every registry transport and the
120
+ * publish op call this — all are already async, so awaiting here adds
121
+ * no restructuring on the caller side; see `loadOxcParser`'s doc for
122
+ * why this can't stay synchronous.
97
123
  *
98
124
  * Static gates only — the optional runtime probe lives behind a
99
125
  * {@link BlueprintProbeRunner} seam so registry-core stays free of
100
126
  * DOM-emulation deps (happy-dom + react-dom/server land in the caller
101
127
  * that wants the probe).
102
128
  */
103
- export function checkConformance(payload) {
129
+ export async function checkConformance(payload) {
104
130
  // ── Gate 1: manifest schema ────────────────────────────────────────
105
131
  const manifestResult = safeParseArtifactManifest(payload.manifest);
106
132
  if (!manifestResult.success) {
@@ -120,7 +146,7 @@ export function checkConformance(payload) {
120
146
  * Extracted from {@link checkConformance} so the blueprint branch can
121
147
  * stand on its own without nesting.
122
148
  */
123
- function checkGadgetConformance(manifest, bundle) {
149
+ async function checkGadgetConformance(manifest, bundle) {
124
150
  // ── Gate 2: bundle is parseable ESM ────────────────────────────────
125
151
  if (typeof bundle !== 'string' || bundle.length === 0) {
126
152
  return {
@@ -133,6 +159,7 @@ function checkGadgetConformance(manifest, bundle) {
133
159
  ],
134
160
  };
135
161
  }
162
+ const { parseSync } = await loadOxcParser();
136
163
  const parsed = parseSync('bundle.js', bundle, {
137
164
  lang: 'js',
138
165
  sourceType: 'module',
@@ -204,7 +231,7 @@ function checkGadgetConformance(manifest, bundle) {
204
231
  * shape. Static-only; the runtime probe lives behind the optional
205
232
  * {@link BlueprintProbeRunner} seam.
206
233
  */
207
- function checkBlueprintConformance(manifest) {
234
+ async function checkBlueprintConformance(manifest) {
208
235
  // ── Gate 5: source size ──────────────────────────────────────────
209
236
  const sourceBytes = utf8ByteLength(manifest.source);
210
237
  if (sourceBytes > MAX_BLUEPRINT_SOURCE_BYTES) {
@@ -225,6 +252,7 @@ function checkBlueprintConformance(manifest) {
225
252
  // (not a compiled output) is deliberate — author intent (including
226
253
  // unused imports that signal mis-configured deps) is what the
227
254
  // allow-list gates, not whatever a bundler would optimize away.
255
+ const { parseSync } = await loadOxcParser();
228
256
  const parsed = parseSync('blueprint.tsx', manifest.source, {
229
257
  lang: 'tsx',
230
258
  sourceType: 'module',
@@ -353,7 +353,7 @@ export async function publishArtifact(input, deps) {
353
353
  const conformanceBundleText = bundleBytes === undefined
354
354
  ? undefined
355
355
  : Buffer.from(bundleBytes.buffer, bundleBytes.byteOffset, bundleBytes.byteLength).toString('utf8');
356
- const conformanceResult = checkConformance({
356
+ const conformanceResult = await checkConformance({
357
357
  manifest,
358
358
  bundle: conformanceBundleText,
359
359
  });
@@ -6,8 +6,11 @@
6
6
  *
7
7
  * Visibility: only `visibility: "public"` rows are exposed. Without that
8
8
  * filter the public `/search` route would leak private artifact IDs.
9
- * Impls SHOULD apply the visibility filter inside `scanArtifacts`; the
10
- * op verifies defensively after the page returns.
9
+ * Impls MUST apply the visibility filter inside `scanArtifacts` — the
10
+ * normative obligation on {@link RegistryStorage.scanArtifacts} (#474);
11
+ * this op's post-fetch check is defense-in-depth ONLY, never the sole
12
+ * gate (a private row that reaches the storage-layer page would
13
+ * consume a `limit` slot before this filter ever discards it).
11
14
  */
12
15
  import type { SearchErrorBody, SearchResponse } from '../types.js';
13
16
  import type { RegistryStorage } from '../interfaces/registry-storage.js';
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/ops/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAKV,eAAe,EACf,cAAc,EAGf,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAOzE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GAC1E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAEvF,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAwHhC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/ops/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAKV,eAAe,EACf,cAAc,EAGf,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAOzE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GAC1E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAEvF,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAwHhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry-storage-contract.d.ts","sourceRoot":"","sources":["../../src/testing/registry-storage-contract.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,mCAAmC,CAAC;AAqI3C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,CAgzBhF"}
1
+ {"version":3,"file":"registry-storage-contract.d.ts","sourceRoot":"","sources":["../../src/testing/registry-storage-contract.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,mCAAmC,CAAC;AAqI3C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,CA01BhF"}
@@ -161,6 +161,32 @@ export function registryStorageContract(makeStorage) {
161
161
  const ids = page.rows.map((r) => r.artifactId).sort();
162
162
  expect(ids).toEqual(['@a/1', '@b/2']);
163
163
  });
164
+ // #474 — visibility gating is ruled to live INSIDE every
165
+ // scanArtifacts impl (the search op's post-filter stays as
166
+ // defense-in-depth, but must never be the ONLY gate — otherwise
167
+ // private rows consume page slots on self-host impls, an
168
+ // observable underfill).
169
+ it('excludes private rows from scanArtifacts results (#474)', async () => {
170
+ const storage = makeStorage();
171
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/secret', visibility: 'private' }));
172
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/public', visibility: 'public' }));
173
+ const page = await storage.scanArtifacts({});
174
+ expect(page.rows.map((r) => r.artifactId)).toEqual(['@a/public']);
175
+ });
176
+ it('private rows never consume a page slot — a `limit` is satisfied entirely from public rows (#474)', async () => {
177
+ const storage = makeStorage();
178
+ // Private rows interleaved with public ones. If private rows
179
+ // consumed limit slots (visibility filtered only at the op
180
+ // layer, after pagination), a limit=2 request could return
181
+ // fewer than 2 public rows.
182
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/priv1', visibility: 'private' }));
183
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/pub1', visibility: 'public' }));
184
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/priv2', visibility: 'private' }));
185
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/pub2', visibility: 'public' }));
186
+ await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/priv3', visibility: 'private' }));
187
+ const page = await storage.scanArtifacts({ limit: 2 });
188
+ expect(page.rows.map((r) => r.artifactId).sort()).toEqual(['@a/pub1', '@a/pub2']);
189
+ });
164
190
  it('filters by kind', async () => {
165
191
  const storage = makeStorage();
166
192
  await storage.putArtifactMetadata(makeMetadata({ artifactId: '@a/g', kind: 'gadget' }));
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Memoizing wrapper around a lazy async load. Caches the in-flight (or
3
+ * already-resolved) promise so concurrent and later callers share one
4
+ * load — but clears the cache on rejection, so a failed attempt
5
+ * doesn't poison every later call for the rest of the process.
6
+ *
7
+ * A rejected promise is still a defined value, so a bare
8
+ * `cached ??= loader()` would cache the failure permanently: every
9
+ * later call would re-throw the same stale rejection even once
10
+ * whatever caused it (a transient resolution failure, a missing
11
+ * optional dependency on first use, …) is no longer true. This
12
+ * wrapper resets the cache to `undefined` on rejection so the next
13
+ * call starts a fresh attempt, while still deduping callers that
14
+ * arrive while an attempt is in flight.
15
+ */
16
+ export declare function memoizedRetryingImport<T>(loader: () => Promise<T>): () => Promise<T>;
17
+ //# sourceMappingURL=lazy-import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-import.d.ts","sourceRoot":"","sources":["../../src/utils/lazy-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,CAWpF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Memoizing wrapper around a lazy async load. Caches the in-flight (or
3
+ * already-resolved) promise so concurrent and later callers share one
4
+ * load — but clears the cache on rejection, so a failed attempt
5
+ * doesn't poison every later call for the rest of the process.
6
+ *
7
+ * A rejected promise is still a defined value, so a bare
8
+ * `cached ??= loader()` would cache the failure permanently: every
9
+ * later call would re-throw the same stale rejection even once
10
+ * whatever caused it (a transient resolution failure, a missing
11
+ * optional dependency on first use, …) is no longer true. This
12
+ * wrapper resets the cache to `undefined` on rejection so the next
13
+ * call starts a fresh attempt, while still deduping callers that
14
+ * arrive while an attempt is in flight.
15
+ */
16
+ export function memoizedRetryingImport(loader) {
17
+ let cached;
18
+ return () => {
19
+ if (cached === undefined) {
20
+ cached = loader().catch((err) => {
21
+ cached = undefined;
22
+ throw err;
23
+ });
24
+ }
25
+ return cached;
26
+ };
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/registry-core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Pure-TypeScript registry operations and storage interfaces for the ggui marketplace. No I/O, no HTTP — operations take typed inputs plus a storage deps bag and return discriminated-union results.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -54,9 +54,9 @@
54
54
  "esbuild": "0.25.12",
55
55
  "oxc-parser": "^0.131.0",
56
56
  "zod": "^4.3.6",
57
- "@ggui-ai/artifact-manifest": "0.8.0",
58
- "@ggui-ai/gadget-signing": "0.8.0",
59
- "@ggui-ai/protocol": "0.8.0"
57
+ "@ggui-ai/protocol": "0.9.0",
58
+ "@ggui-ai/artifact-manifest": "0.9.0",
59
+ "@ggui-ai/gadget-signing": "0.9.0"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "vitest": {
@@ -71,7 +71,7 @@
71
71
  "nock": "^13.5.6",
72
72
  "typescript": "^5.0.0",
73
73
  "vitest": "^3.2.6",
74
- "@ggui-ai/protocol-conformance": "0.8.0"
74
+ "@ggui-ai/protocol-conformance": "0.9.0"
75
75
  },
76
76
  "repository": {
77
77
  "type": "git",