@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.
- package/dist/impls/memory-registry-storage.js +7 -0
- package/dist/interfaces/registry-storage.d.ts +13 -2
- package/dist/interfaces/registry-storage.d.ts.map +1 -1
- package/dist/ops/compile.d.ts.map +1 -1
- package/dist/ops/compile.js +19 -1
- package/dist/ops/conformance.d.ts +8 -5
- package/dist/ops/conformance.d.ts.map +1 -1
- package/dist/ops/conformance.js +35 -7
- package/dist/ops/publish.js +1 -1
- package/dist/ops/search.d.ts +5 -2
- package/dist/ops/search.d.ts.map +1 -1
- package/dist/testing/registry-storage-contract.d.ts.map +1 -1
- package/dist/testing/registry-storage-contract.js +26 -0
- package/dist/utils/lazy-import.d.ts +17 -0
- package/dist/utils/lazy-import.d.ts.map +1 -0
- package/dist/utils/lazy-import.js +27 -0
- package/package.json +5 -5
|
@@ -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
|
-
*
|
|
77
|
-
*
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/ops/compile.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/ops/compile.js
CHANGED
|
@@ -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
|
|
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 {
|
|
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
|
|
120
|
-
* network, no env lookups.
|
|
121
|
-
* Every registry transport and the
|
|
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,
|
|
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"}
|
package/dist/ops/conformance.js
CHANGED
|
@@ -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 {
|
|
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
|
|
95
|
-
* network, no env lookups.
|
|
96
|
-
* Every registry transport and the
|
|
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',
|
package/dist/ops/publish.js
CHANGED
|
@@ -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
|
});
|
package/dist/ops/search.d.ts
CHANGED
|
@@ -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
|
|
10
|
-
*
|
|
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';
|
package/dist/ops/search.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/ops/search.ts"],"names":[],"mappings":"AAAA
|
|
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,
|
|
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.
|
|
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/
|
|
58
|
-
"@ggui-ai/
|
|
59
|
-
"@ggui-ai/
|
|
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.
|
|
74
|
+
"@ggui-ai/protocol-conformance": "0.9.0"
|
|
75
75
|
},
|
|
76
76
|
"repository": {
|
|
77
77
|
"type": "git",
|