@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.
- package/dist/impls/memory-registry-storage.d.ts.map +1 -1
- package/dist/impls/memory-registry-storage.js +44 -4
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/install-command.d.ts +25 -0
- package/dist/install-command.d.ts.map +1 -0
- package/dist/install-command.js +15 -0
- package/dist/interfaces/registry-storage.d.ts +95 -1
- package/dist/interfaces/registry-storage.d.ts.map +1 -1
- package/dist/ops/delete-author-key.d.ts +45 -0
- package/dist/ops/delete-author-key.d.ts.map +1 -0
- package/dist/ops/delete-author-key.js +31 -0
- package/dist/ops/list-author-keys.d.ts +34 -0
- package/dist/ops/list-author-keys.d.ts.map +1 -0
- package/dist/ops/list-author-keys.js +45 -0
- package/dist/ops/publish.d.ts +38 -1
- package/dist/ops/publish.d.ts.map +1 -1
- package/dist/ops/publish.js +145 -3
- package/dist/ops/register-author-key.d.ts +13 -0
- package/dist/ops/register-author-key.d.ts.map +1 -1
- package/dist/ops/register-author-key.js +42 -0
- package/dist/testing/registry-storage-contract.d.ts +1 -1
- package/dist/testing/registry-storage-contract.d.ts.map +1 -1
- package/dist/testing/registry-storage-contract.js +229 -0
- package/dist/types.d.ts +143 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +49 -0
- package/package.json +13 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-registry-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-registry-storage.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
109
|
-
if (
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
+
}
|
package/dist/ops/publish.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|