@ggui-ai/registry-core 0.6.2 → 0.7.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.
@@ -1 +1 @@
1
- {"version":3,"file":"memory-registry-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-registry-storage.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,mCAAmC,CAAC;AAK3C,wBAAgB,uBAAuB,IAAI,eAAe,CAmHzD"}
1
+ {"version":3,"file":"memory-registry-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-registry-storage.ts"],"names":[],"mappings":"AAmBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,mCAAmC,CAAC;AAK3C,wBAAgB,uBAAuB,IAAI,eAAe,CA2JzD"}
@@ -6,8 +6,12 @@ export function inMemoryRegistryStorage() {
6
6
  const versions = new Map();
7
7
  const authorKeys = new Map();
8
8
  const compiledBlobs = new Map();
9
+ const scopeOwners = new Map();
9
10
  const versionKey = (artifactId, version) => `${artifactId}@${version}`;
10
- const authorKey = (subject, keyId) => `${subject}/${keyId}`;
11
+ // JSON tuple — unambiguous for ANY subject/keyId contents. Subjects
12
+ // are operator-defined free text, so a `${subject}/${keyId}` composite
13
+ // would make ('team', …) and ('team/alice', …) collide or leak.
14
+ const authorKey = (subject, keyId) => JSON.stringify([subject, keyId]);
11
15
  return {
12
16
  async getArtifactMetadata(artifactId) {
13
17
  return metadata.get(artifactId) ?? null;
@@ -92,6 +96,36 @@ export function inMemoryRegistryStorage() {
92
96
  });
93
97
  return { ok: true, mode: 'dedup' };
94
98
  },
99
+ async getScopeOwner(scope) {
100
+ return scopeOwners.get(scope) ?? null;
101
+ },
102
+ async claimScope(row) {
103
+ // First-writer-wins. The check-and-set pair runs with no `await`
104
+ // between them, so the single-threaded event loop makes it
105
+ // atomic — two racing claims resolve strictly one-after-another.
106
+ if (scopeOwners.has(row.scope)) {
107
+ return { conflict: true };
108
+ }
109
+ scopeOwners.set(row.scope, row);
110
+ return { ok: true };
111
+ },
112
+ async updateScopeOwner(row, expect) {
113
+ // Compare-and-set against the caller's read snapshot. The
114
+ // check-and-set pair runs with no interleaving `await`, so the
115
+ // event loop makes it atomic (same discipline as claimScope).
116
+ const current = scopeOwners.get(row.scope);
117
+ if ('absent' in expect) {
118
+ if (current !== undefined)
119
+ return { conflict: true };
120
+ }
121
+ else if (current === undefined ||
122
+ current.ownerSubject !== expect.ownerSubject ||
123
+ current.verification !== expect.verification) {
124
+ return { conflict: true };
125
+ }
126
+ scopeOwners.set(row.scope, row);
127
+ return { ok: true };
128
+ },
95
129
  async getAuthorKey(subject, keyId) {
96
130
  return authorKeys.get(authorKey(subject, keyId)) ?? null;
97
131
  },
@@ -103,14 +137,20 @@ export function inMemoryRegistryStorage() {
103
137
  authorKeys.set(key, row);
104
138
  },
105
139
  async listAuthorKeys(subject) {
106
- const prefix = `${subject}/`;
140
+ // Row-field filter, not key-prefix matching — the row's own
141
+ // `subject` is the only unambiguous ownership signal.
107
142
  const matches = [];
108
- for (const [key, row] of authorKeys) {
109
- if (key.startsWith(prefix))
143
+ for (const row of authorKeys.values()) {
144
+ if (row.subject === subject)
110
145
  matches.push(row);
111
146
  }
112
147
  return matches;
113
148
  },
149
+ async deleteAuthorKey(subject, keyId) {
150
+ // Map#delete returns whether an entry existed — exactly the
151
+ // contract's `deleted` signal.
152
+ return authorKeys.delete(authorKey(subject, keyId));
153
+ },
114
154
  };
115
155
  }
116
156
  function clampLimit(limit) {
package/dist/index.d.ts CHANGED
@@ -9,17 +9,20 @@
9
9
  */
10
10
  export type { AuthnContext } from './interfaces/authn.js';
11
11
  export type { BundleStorage } from './interfaces/bundle-storage.js';
12
- export { AuthorKeyAlreadyExistsError, type PutAuthorKeyOptions, type RegistryStorage, } from './interfaces/registry-storage.js';
13
- export { ARTIFACTS_METADATA_SK, type ArtifactKind, type ArtifactScanFilter, type ArtifactVersionRow, type ArtifactsMetadataRow, type AuthorKeyRow, type CompiledBlobRow, type ErrorBody, LIST_VERSIONS_ERROR_CODES, type ListVersionsErrorBody, type ListVersionsErrorCode, type ListVersionsResponse, PUBLISH_ERROR_CODES, type PublishErrorBody, type PublishErrorCode, type PublishRequestBody, type PublishResponseBody, READ_ERROR_CODES, type ReadErrorBody, type ReadErrorCode, type ReadPkgResponse, REGISTER_AUTHOR_KEY_ERROR_CODES, type RegisterAuthorKeyErrorBody, type RegisterAuthorKeyErrorCode, type RegisterAuthorKeyRequestBody, type RegisterAuthorKeyResponseBody, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, type SearchErrorBody, type SearchErrorCode, type SearchResponse, type SearchResultEntry, type SearchSort, type VersionListEntry, type Visibility, } from './types.js';
12
+ export { AuthorKeyAlreadyExistsError, type PutAuthorKeyOptions, type RegistryStorage, type ScopeOwnerExpectation, } from './interfaces/registry-storage.js';
13
+ export { ARTIFACTS_METADATA_SK, type ArtifactKind, type ArtifactScanFilter, type ArtifactVersionRow, type ArtifactsMetadataRow, type AuthorKeyListEntry, type AuthorKeyRow, type CompiledBlobRow, DELETE_AUTHOR_KEY_ERROR_CODES, type DeleteAuthorKeyErrorBody, type DeleteAuthorKeyErrorCode, type DeleteAuthorKeyResponseBody, type ErrorBody, LIST_AUTHOR_KEYS_ERROR_CODES, type ListAuthorKeysErrorBody, type ListAuthorKeysErrorCode, type ListAuthorKeysResponseBody, LIST_VERSIONS_ERROR_CODES, type ListVersionsErrorBody, type ListVersionsErrorCode, type ListVersionsResponse, PUBLISH_ERROR_CODES, type PublishErrorBody, type PublishErrorCode, type PublishRequestBody, type PublishResponseBody, READ_ERROR_CODES, type ReadErrorBody, type ReadErrorCode, type ReadPkgResponse, REGISTER_AUTHOR_KEY_ERROR_CODES, type RegisterAuthorKeyErrorBody, type RegisterAuthorKeyErrorCode, type RegisterAuthorKeyRequestBody, type RegisterAuthorKeyResponseBody, SCOPE_VERIFICATIONS, type ScopeOwnerRow, type ScopeVerification, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, unauthorizedErrorBody, type SearchErrorBody, type SearchErrorCode, type SearchResponse, type SearchResultEntry, type SearchSort, type VersionListEntry, type Visibility, } from './types.js';
14
14
  export { compileBlueprint, type CompileBlueprintErr, type CompileBlueprintOk, type CompileBlueprintResult, } from './ops/compile.js';
15
15
  export { checkConformance, MAX_BLUEPRINT_SOURCE_BYTES, type BlueprintProbeRunner, type ConformanceError, type ConformanceErrorCode, type ConformanceFailureCode, type ConformanceRequestPayload, type ConformanceResponseBody, } from './ops/conformance.js';
16
- export { publishArtifact, MAX_BUNDLE_BYTES, type PublishArtifactDeps, type PublishArtifactInput, type PublishArtifactResult, } from './ops/publish.js';
16
+ export { publishArtifact, MAX_BUNDLE_BYTES, RESERVED_SCOPES, type PublishArtifactDeps, type PublishArtifactInput, type PublishArtifactResult, } from './ops/publish.js';
17
17
  export { readArtifact, type ReadArtifactDeps, type ReadArtifactInput, type ReadArtifactResult, } from './ops/read.js';
18
18
  export { listArtifactVersions, type ListArtifactVersionsDeps, type ListArtifactVersionsInput, type ListArtifactVersionsResult, } from './ops/list-versions.js';
19
19
  export { searchArtifacts, type SearchArtifactsDeps, type SearchArtifactsInput, type SearchArtifactsResult, } from './ops/search.js';
20
20
  export { registerAuthorKey, type RegisterAuthorKeyDeps, type RegisterAuthorKeyInput, type RegisterAuthorKeyResult, } from './ops/register-author-key.js';
21
+ export { listAuthorKeys, type ListAuthorKeysDeps, type ListAuthorKeysResult, } from './ops/list-author-keys.js';
22
+ export { deleteAuthorKey, type DeleteAuthorKeyDeps, type DeleteAuthorKeyInput, type DeleteAuthorKeyResult, } from './ops/delete-author-key.js';
21
23
  export { inMemoryRegistryStorage } from './impls/memory-registry-storage.js';
22
24
  export { inMemoryBundleStorage, type InMemoryBundleStorageOptions, } from './impls/memory-bundle-storage.js';
25
+ export { installCommand, type InstallCommandOptions, } from './install-command.js';
23
26
  export { compareSemver } from './utils/semver.js';
24
27
  export { base64Encode, safeBase64Decode, sha384Base64 } from './utils/base64.js';
25
28
  export type { Ed25519Signature, GadgetSignature } from '@ggui-ai/gadget-signing';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,GAChC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKjF,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -9,18 +9,21 @@
9
9
  */
10
10
  export { AuthorKeyAlreadyExistsError, } from './interfaces/registry-storage.js';
11
11
  // Types — rows + wire shapes
12
- export { ARTIFACTS_METADATA_SK, LIST_VERSIONS_ERROR_CODES, PUBLISH_ERROR_CODES, READ_ERROR_CODES, REGISTER_AUTHOR_KEY_ERROR_CODES, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, } from './types.js';
12
+ export { ARTIFACTS_METADATA_SK, DELETE_AUTHOR_KEY_ERROR_CODES, LIST_AUTHOR_KEYS_ERROR_CODES, LIST_VERSIONS_ERROR_CODES, PUBLISH_ERROR_CODES, READ_ERROR_CODES, REGISTER_AUTHOR_KEY_ERROR_CODES, SCOPE_VERIFICATIONS, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, unauthorizedErrorBody, } from './types.js';
13
13
  // Operations
14
14
  export { compileBlueprint, } from './ops/compile.js';
15
15
  export { checkConformance, MAX_BLUEPRINT_SOURCE_BYTES, } from './ops/conformance.js';
16
- export { publishArtifact, MAX_BUNDLE_BYTES, } from './ops/publish.js';
16
+ export { publishArtifact, MAX_BUNDLE_BYTES, RESERVED_SCOPES, } from './ops/publish.js';
17
17
  export { readArtifact, } from './ops/read.js';
18
18
  export { listArtifactVersions, } from './ops/list-versions.js';
19
19
  export { searchArtifacts, } from './ops/search.js';
20
20
  export { registerAuthorKey, } from './ops/register-author-key.js';
21
+ export { listAuthorKeys, } from './ops/list-author-keys.js';
22
+ export { deleteAuthorKey, } from './ops/delete-author-key.js';
21
23
  // In-memory impls
22
24
  export { inMemoryRegistryStorage } from './impls/memory-registry-storage.js';
23
25
  export { inMemoryBundleStorage, } from './impls/memory-bundle-storage.js';
24
26
  // Utils
27
+ export { installCommand, } from './install-command.js';
25
28
  export { compareSemver } from './utils/semver.js';
26
29
  export { base64Encode, safeBase64Decode, sha384Base64 } from './utils/base64.js';
@@ -0,0 +1,25 @@
1
+ import type { ArtifactKind } from './types.js';
2
+ export interface InstallCommandOptions {
3
+ /**
4
+ * Registry origin to pin via an explicit `--registry=<url>` flag —
5
+ * pass the URL the artifact was discovered on (the same origin the
6
+ * search/read endpoints were queried against). When provided, the
7
+ * emitted command installs from exactly that registry instead of
8
+ * whatever the CLI's ambient default resolves to; when omitted, the
9
+ * flag is left off and the CLI default applies.
10
+ */
11
+ readonly registryHost?: string;
12
+ }
13
+ /**
14
+ * Compose the canonical CLI install command for a published artifact:
15
+ *
16
+ * ggui <kind> install <scope>/<name>@<version> [--registry=<url>]
17
+ *
18
+ * Same shape the publish flow reports back after a successful publish,
19
+ * so every surface that displays an install command agrees on one
20
+ * wording. Pure string composition — callers are responsible for
21
+ * validating `artifactId` (`@scope/name`) and `version` (semver)
22
+ * before display; this function does not re-validate.
23
+ */
24
+ export declare function installCommand(kind: ArtifactKind, artifactId: string, version: string, opts?: InstallCommandOptions): string;
25
+ //# sourceMappingURL=install-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-command.d.ts","sourceRoot":"","sources":["../src/install-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,qBAA0B,GAC/B,MAAM,CAIR"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Compose the canonical CLI install command for a published artifact:
3
+ *
4
+ * ggui <kind> install <scope>/<name>@<version> [--registry=<url>]
5
+ *
6
+ * Same shape the publish flow reports back after a successful publish,
7
+ * so every surface that displays an install command agrees on one
8
+ * wording. Pure string composition — callers are responsible for
9
+ * validating `artifactId` (`@scope/name`) and `version` (semver)
10
+ * before display; this function does not re-validate.
11
+ */
12
+ export function installCommand(kind, artifactId, version, opts = {}) {
13
+ const registry = opts.registryHost !== undefined ? ` --registry=${opts.registryHost}` : '';
14
+ return `ggui ${kind} install ${artifactId}@${version}${registry}`;
15
+ }
@@ -27,6 +27,23 @@
27
27
  * at the storage layer. Implementations
28
28
  * MUST NOT overwrite on conflict — per-version immutability is a
29
29
  * load-bearing registry invariant.
30
+ * - {@link claimScope} MUST be an atomic first-writer-wins conditional
31
+ * create: when two claims race on the same scope, EXACTLY one wins
32
+ * and the loser observes `{ conflict: true }` with the winner's row
33
+ * left untouched. Scope ownership is the publish-authorization
34
+ * invariant — a lost race that overwrote the winner would silently
35
+ * reassign every future publish under that scope.
36
+ * - {@link getScopeOwner} MUST observe a completed {@link claimScope}
37
+ * immediately (read-your-writes strong). The publish gate's
38
+ * claim-conflict path re-reads to learn the winner; a read that can
39
+ * lag a durable claim would fail the losing racer's publish with a
40
+ * spurious storage-inconsistency error.
41
+ * - {@link updateScopeOwner} MUST be conditional on the caller's read
42
+ * snapshot (`expect`): the write lands only when the stored row still
43
+ * matches (or is still absent, for `{ absent: true }`). On mismatch
44
+ * it returns `{ conflict: true }` with the stored row untouched —
45
+ * an operator rewrite MUST NOT clobber a concurrent transfer or a
46
+ * racing first-publish claim.
30
47
  * - {@link getArtifactMetadata} / {@link getArtifactVersion} MUST return
31
48
  * exactly what was last written. `null` on miss; throw on transport
32
49
  * failure (caller decides retry).
@@ -48,10 +65,11 @@
48
65
  * round-trip preservation, idempotent {@link putArtifactMetadata},
49
66
  * `putArtifactVersionIfAbsent` rejects on collision, missing returns
50
67
  * null, `listAuthorKeys` returns only keys for the queried subject,
68
+ * `deleteAuthorKey` true/false idempotency + per-subject isolation,
51
69
  * and `order: 'recent'` newest-first ordering (single page + across
52
70
  * the cursor chain).
53
71
  */
54
- import type { ArtifactScanFilter, ArtifactVersionRow, ArtifactsMetadataRow, AuthorKeyRow, CompiledBlobRow } from '../types.js';
72
+ import type { ArtifactScanFilter, ArtifactVersionRow, ArtifactsMetadataRow, AuthorKeyRow, CompiledBlobRow, ScopeOwnerRow } from '../types.js';
55
73
  /**
56
74
  * Optional flags for {@link RegistryStorage.putAuthorKey}.
57
75
  *
@@ -79,6 +97,18 @@ export declare class AuthorKeyAlreadyExistsError extends Error {
79
97
  readonly keyId: string;
80
98
  constructor(subject: string, keyId: string);
81
99
  }
100
+ /**
101
+ * The caller's read snapshot for {@link RegistryStorage.updateScopeOwner}
102
+ * — either the `(ownerSubject, verification)` pair the caller read, or
103
+ * an assertion that no row exists yet (`{ absent: true }`, the operator
104
+ * seed path).
105
+ */
106
+ export type ScopeOwnerExpectation = {
107
+ readonly ownerSubject: string;
108
+ readonly verification: ScopeOwnerRow['verification'];
109
+ } | {
110
+ readonly absent: true;
111
+ };
82
112
  export interface RegistryStorage {
83
113
  getArtifactMetadata(artifactId: string): Promise<ArtifactsMetadataRow | null>;
84
114
  putArtifactMetadata(row: ArtifactsMetadataRow): Promise<void>;
@@ -201,6 +231,56 @@ export interface RegistryStorage {
201
231
  ok: false;
202
232
  reason: 'version_exists';
203
233
  }>;
234
+ /**
235
+ * Fetch the ownership row for `scope` (leading `@` included, e.g.
236
+ * `@acme`). Returns `null` when the scope is unclaimed.
237
+ *
238
+ * MUST be read-your-writes strong with respect to {@link claimScope}
239
+ * and {@link updateScopeOwner} — see the Obligations section above.
240
+ */
241
+ getScopeOwner(scope: string): Promise<ScopeOwnerRow | null>;
242
+ /**
243
+ * Atomic first-writer-wins claim — creates the ownership row only
244
+ * when no row exists for `row.scope`. On conflict (a row already
245
+ * exists, including one that landed in a concurrent race) returns
246
+ * `{ conflict: true }` WITHOUT touching the existing row.
247
+ *
248
+ * The publish op claims on the caller's first publish into an
249
+ * unclaimed scope. Consumers MUST NOT pre-check with
250
+ * {@link getScopeOwner} + claim as a substitute for handling
251
+ * `conflict` — the conditional create is the only race-safe
252
+ * primitive (re-read on conflict instead).
253
+ */
254
+ claimScope(row: ScopeOwnerRow): Promise<{
255
+ ok: true;
256
+ } | {
257
+ conflict: true;
258
+ }>;
259
+ /**
260
+ * Conditional full-row put of the ownership row — a compare-and-set
261
+ * against the caller's read snapshot.
262
+ *
263
+ * `expect` states what the caller believes is stored:
264
+ * - `{ ownerSubject, verification }` — the row MUST still carry
265
+ * exactly this pair (the snapshot identity of an ownership row).
266
+ * - `{ absent: true }` — no row may exist yet (operator seeding).
267
+ *
268
+ * On mismatch the write is refused with `{ conflict: true }` and the
269
+ * stored row is untouched — the caller re-reads and retries. This is
270
+ * the structural defense against read-modify-write races between two
271
+ * operators, or between an operator write and a concurrent
272
+ * first-publish claim.
273
+ *
274
+ * Operator-only caller: verification flips and ownership transfers
275
+ * (including seeding rows for reserved scopes, which are never
276
+ * first-publish-claimable). The publish path MUST NOT call this —
277
+ * {@link claimScope} is its only write.
278
+ */
279
+ updateScopeOwner(row: ScopeOwnerRow, expect: ScopeOwnerExpectation): Promise<{
280
+ ok: true;
281
+ } | {
282
+ conflict: true;
283
+ }>;
204
284
  getAuthorKey(subject: string, keyId: string): Promise<AuthorKeyRow | null>;
205
285
  /**
206
286
  * Write an AuthorKey row. With `options.ifNotExists === true`, the
@@ -211,5 +291,19 @@ export interface RegistryStorage {
211
291
  */
212
292
  putAuthorKey(row: AuthorKeyRow, options?: PutAuthorKeyOptions): Promise<void>;
213
293
  listAuthorKeys(subject: string): Promise<readonly AuthorKeyRow[]>;
294
+ /**
295
+ * Hard-delete the row for `(subject, keyId)`. Returns `true` when a
296
+ * row existed and was removed, `false` when no row existed — the
297
+ * caller-visible idempotency signal for the delete op's
298
+ * `deleted: false` response. Implementations MUST NOT throw on the
299
+ * absent case; transport failures still throw.
300
+ *
301
+ * Hard delete is the registry's established author-key removal
302
+ * semantic: the durable audit trail is the public key pinned per
303
+ * published version (`ArtifactVersionRow.authorPublicKey` +
304
+ * `publishedBy`), which this delete never touches — removal only
305
+ * blocks FUTURE publishes signed with the key.
306
+ */
307
+ deleteAuthorKey(subject: string, keyId: string): Promise<boolean>;
214
308
  }
215
309
  //# sourceMappingURL=registry-storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EAChB,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,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;;;;;;;;;;OAUG;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,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;CACnE"}
1
+ {"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `deleteAuthorKey` — pure op for `DELETE /author-keys/{keyId}`.
3
+ *
4
+ * Hard-deletes the `(subject, keyId)` row under the VERIFIED caller
5
+ * identity. Like {@link listAuthorKeys} there is no anonymous branch —
6
+ * transports MUST 401 before calling this op when the bearer
7
+ * credential is missing or invalid.
8
+ *
9
+ * Semantics (see {@link DeleteAuthorKeyResponseBody}):
10
+ *
11
+ * - Idempotent: an absent keyId is a 200 with `deleted: false`, not
12
+ * an error — retries and double-clicks converge.
13
+ * - Subject-scoped: the delete only ever addresses the caller's own
14
+ * partition. A keyId registered by ANOTHER subject is untouched
15
+ * and the response is byte-identical to the plain-absent case, so
16
+ * key existence never leaks across accounts.
17
+ * - Forward-only: removal blocks FUTURE publishes signed with this
18
+ * key. Versions already published stay valid — their signing key
19
+ * is pinned per version at publish time, which this delete never
20
+ * touches.
21
+ */
22
+ import type { DeleteAuthorKeyErrorBody, DeleteAuthorKeyResponseBody } from '../types.js';
23
+ import type { AuthnContext } from '../interfaces/authn.js';
24
+ import type { RegistryStorage } from '../interfaces/registry-storage.js';
25
+ export interface DeleteAuthorKeyInput {
26
+ /** The keyId path segment — `derivePublicKeyId` output, non-empty. */
27
+ readonly keyId: string;
28
+ }
29
+ export interface DeleteAuthorKeyDeps {
30
+ readonly storage: RegistryStorage;
31
+ /** REQUIRED — the verified caller identity. No anonymous branch. */
32
+ readonly authn: AuthnContext;
33
+ }
34
+ export type DeleteAuthorKeyResult = {
35
+ readonly ok: true;
36
+ readonly status: 200;
37
+ readonly body: DeleteAuthorKeyResponseBody;
38
+ } | {
39
+ readonly ok: false;
40
+ readonly status: 400 | 500;
41
+ readonly body: DeleteAuthorKeyErrorBody;
42
+ };
43
+ export declare function deleteAuthorKey(input: DeleteAuthorKeyInput, deps: DeleteAuthorKeyDeps): Promise<DeleteAuthorKeyResult>;
44
+ export type { DeleteAuthorKeyErrorBody, DeleteAuthorKeyResponseBody };
45
+ //# sourceMappingURL=delete-author-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-author-key.d.ts","sourceRoot":"","sources":["../../src/ops/delete-author-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;CAC5C,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;CACzC,CAAC;AAEN,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CA+BhC;AAID,YAAY,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,CAAC"}
@@ -0,0 +1,31 @@
1
+ export async function deleteAuthorKey(input, deps) {
2
+ if (typeof input.keyId !== 'string' || input.keyId.length === 0) {
3
+ return {
4
+ ok: false,
5
+ status: 400,
6
+ body: {
7
+ error: 'invalid_request',
8
+ message: '`keyId` path segment is required',
9
+ },
10
+ };
11
+ }
12
+ let deleted;
13
+ try {
14
+ deleted = await deps.storage.deleteAuthorKey(deps.authn.subject, input.keyId);
15
+ }
16
+ catch (err) {
17
+ return {
18
+ ok: false,
19
+ status: 500,
20
+ body: {
21
+ error: 'server_error',
22
+ message: `failed to delete author key: ${err instanceof Error ? err.message : String(err)}`,
23
+ },
24
+ };
25
+ }
26
+ return {
27
+ ok: true,
28
+ status: 200,
29
+ body: { keyId: input.keyId, deleted },
30
+ };
31
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `listAuthorKeys` — pure op for `GET /author-keys`.
3
+ *
4
+ * Lists every signing key registered under the VERIFIED caller
5
+ * identity. Unlike the read/list-versions ops there is no anonymous
6
+ * branch: "my keys" has no public projection, so `deps.authn` is
7
+ * required — transports MUST 401 before calling this op when the
8
+ * bearer credential is missing or invalid.
9
+ *
10
+ * Ordering: newest first by `createdAt` (ISO strings compare
11
+ * lexicographically === chronologically), rows without `createdAt`
12
+ * (registered before the display enrichment) last, `keyId` tie-break —
13
+ * deterministic across storage impls whose list order is arbitrary.
14
+ */
15
+ import type { ListAuthorKeysErrorBody, ListAuthorKeysResponseBody } from '../types.js';
16
+ import type { AuthnContext } from '../interfaces/authn.js';
17
+ import type { RegistryStorage } from '../interfaces/registry-storage.js';
18
+ export interface ListAuthorKeysDeps {
19
+ readonly storage: RegistryStorage;
20
+ /** REQUIRED — the verified caller identity. No anonymous branch. */
21
+ readonly authn: AuthnContext;
22
+ }
23
+ export type ListAuthorKeysResult = {
24
+ readonly ok: true;
25
+ readonly status: 200;
26
+ readonly body: ListAuthorKeysResponseBody;
27
+ } | {
28
+ readonly ok: false;
29
+ readonly status: 500;
30
+ readonly body: ListAuthorKeysErrorBody;
31
+ };
32
+ export declare function listAuthorKeys(deps: ListAuthorKeysDeps): Promise<ListAuthorKeysResult>;
33
+ export type { ListAuthorKeysErrorBody, ListAuthorKeysResponseBody };
34
+ //# sourceMappingURL=list-author-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-author-keys.d.ts","sourceRoot":"","sources":["../../src/ops/list-author-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAGV,uBAAuB,EACvB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,MAAM,oBAAoB,GAC5B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;CAC3C,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAEN,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAuB/B;AAyBD,YAAY,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,CAAC"}
@@ -0,0 +1,45 @@
1
+ export async function listAuthorKeys(deps) {
2
+ const subject = deps.authn.subject;
3
+ let rows;
4
+ try {
5
+ rows = await deps.storage.listAuthorKeys(subject);
6
+ }
7
+ catch (err) {
8
+ return {
9
+ ok: false,
10
+ status: 500,
11
+ body: {
12
+ error: 'server_error',
13
+ message: `failed to list author keys: ${err instanceof Error ? err.message : String(err)}`,
14
+ },
15
+ };
16
+ }
17
+ const sorted = [...rows].sort(compareNewestFirst);
18
+ return {
19
+ ok: true,
20
+ status: 200,
21
+ body: { subject, keys: sorted.map(rowToEntry) },
22
+ };
23
+ }
24
+ function compareNewestFirst(a, b) {
25
+ if (a.createdAt !== undefined && b.createdAt !== undefined) {
26
+ const byCreated = b.createdAt.localeCompare(a.createdAt);
27
+ if (byCreated !== 0)
28
+ return byCreated;
29
+ }
30
+ else if (a.createdAt !== undefined) {
31
+ return -1;
32
+ }
33
+ else if (b.createdAt !== undefined) {
34
+ return 1;
35
+ }
36
+ return a.keyId.localeCompare(b.keyId);
37
+ }
38
+ function rowToEntry(row) {
39
+ return {
40
+ keyId: row.keyId,
41
+ publicKeyBase64: row.publicKeyBase64,
42
+ ...(row.createdAt !== undefined ? { createdAt: row.createdAt } : {}),
43
+ ...(row.label !== undefined ? { label: row.label } : {}),
44
+ };
45
+ }
@@ -1,4 +1,4 @@
1
- import { type GadgetSignature } from '@ggui-ai/gadget-signing';
1
+ import { type GadgetSignature, type VerifyBundleSigstoreInput } from '@ggui-ai/gadget-signing';
2
2
  import type { PublishErrorBody, PublishResponseBody } from '../types.js';
3
3
  import type { BlueprintProbeRunner } from './conformance.js';
4
4
  import type { AuthnContext } from '../interfaces/authn.js';
@@ -12,6 +12,22 @@ import type { RegistryStorage } from '../interfaces/registry-storage.js';
12
12
  * against an OSS-mirrored registry without re-bundling).
13
13
  */
14
14
  export declare const MAX_BUNDLE_BYTES: number;
15
+ /**
16
+ * Scopes no publish may CLAIM — well-known names whose squatting would
17
+ * mislead installers about who authored an artifact. The default
18
+ * covers first-party names plus obvious squat-bait; a deployment
19
+ * REPLACES the whole list via
20
+ * {@link PublishArtifactDeps.reservedScopes} (spread this constant to
21
+ * extend it instead).
22
+ *
23
+ * Reserved scopes block first-publish CLAIMS only — not owned
24
+ * publishes. A registry operator who wants artifacts under a reserved
25
+ * name seeds its ownership row out-of-band via
26
+ * {@link RegistryStorage.updateScopeOwner}; once the row exists, the
27
+ * normal owner rule applies (the seeded owner publishes, everyone else
28
+ * gets `scope_forbidden`) with no change to this list.
29
+ */
30
+ export declare const RESERVED_SCOPES: readonly string[];
15
31
  export interface PublishArtifactInput {
16
32
  /** Unvalidated request body — the op parses + validates. */
17
33
  readonly manifest: unknown;
@@ -44,6 +60,13 @@ export interface PublishArtifactDeps {
44
60
  * `http://` for `localhost`/`127.0.0.1`.
45
61
  */
46
62
  readonly registryHostname: string;
63
+ /**
64
+ * Scopes no publish may claim — REPLACES {@link RESERVED_SCOPES}
65
+ * when set (spread the constant to extend instead:
66
+ * `[...RESERVED_SCOPES, '@my-brand']`). Exact-match against
67
+ * `manifest.scope`, leading `@` included.
68
+ */
69
+ readonly reservedScopes?: readonly string[];
47
70
  /**
48
71
  * Optional runtime probe for blueprint manifests. Static gates
49
72
  * always run; the probe additionally compiles + renders the
@@ -56,6 +79,20 @@ export interface PublishArtifactDeps {
56
79
  * HTTP endpoint that should not pay for react-dom.
57
80
  */
58
81
  readonly blueprintProbe?: BlueprintProbeRunner;
82
+ /**
83
+ * Optional TUF trust-root tuning forwarded verbatim to
84
+ * sigstore-cosign signature verification (see
85
+ * {@link VerifyBundleSigstoreInput}). `tufCachePath` points the
86
+ * verifier's trust-root cache at a writable directory — serverless
87
+ * runtimes typically only allow writes under `/tmp`.
88
+ * `tufForceCache` reuses valid cached TUF metadata without a remote
89
+ * refresh. `tufMirrorURL` + `tufRootPath` select an alternative TUF
90
+ * repository (private sigstore deployments; hermetic tests) — the
91
+ * trust root is THE knob that decides which signing infrastructure
92
+ * this registry's verifier trusts. Ed25519 verification never
93
+ * consults any of these.
94
+ */
95
+ readonly sigstoreTuf?: Pick<VerifyBundleSigstoreInput, 'tufCachePath' | 'tufForceCache' | 'tufMirrorURL' | 'tufRootPath'>;
59
96
  }
60
97
  export type PublishArtifactResult = {
61
98
  readonly ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/ops/publish.ts"],"names":[],"mappings":"AA2BA,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAIV,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMzE;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,QAAkB,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;CAChD;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC/E;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC,CAAC;AAEN,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAuXhC"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/ops/publish.ts"],"names":[],"mappings":"AA6CA,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAIV,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMzE;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,QAAkB,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAc5C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;IAC/C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CACzB,yBAAyB,EACzB,cAAc,GAAG,eAAe,GAAG,cAAc,GAAG,aAAa,CAClE,CAAC;CACH;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC/E;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC,CAAC;AAEN,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAqfhC"}