@jini-ai/platform 0.5.2 → 0.5.3

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.
Files changed (57) hide show
  1. package/dist/asset-cache.d.ts +12 -1
  2. package/dist/asset-cache.js +9 -5
  3. package/dist/child-environment.d.ts +18 -0
  4. package/dist/child-environment.js +26 -0
  5. package/dist/fs/boot-identity.d.ts +10 -0
  6. package/dist/fs/boot-identity.js +34 -0
  7. package/dist/fs/directory-home.d.ts +15 -0
  8. package/dist/fs/directory-home.js +17 -0
  9. package/dist/fs/directory-registry.d.ts +50 -0
  10. package/dist/fs/directory-registry.js +99 -0
  11. package/dist/fs/directory-running-lock.d.ts +43 -0
  12. package/dist/fs/directory-running-lock.js +66 -0
  13. package/dist/fs/directory-sync.d.ts +20 -0
  14. package/dist/fs/directory-sync.js +87 -0
  15. package/dist/fs/directory-user-config.d.ts +79 -0
  16. package/dist/fs/directory-user-config.js +183 -0
  17. package/dist/fs/directory-workspace.d.ts +132 -0
  18. package/dist/fs/directory-workspace.js +266 -0
  19. package/dist/fs/durable-json.d.ts +21 -0
  20. package/dist/fs/durable-json.js +59 -4
  21. package/dist/fs/file-lock.d.ts +3 -0
  22. package/dist/fs/file-lock.js +16 -7
  23. package/dist/fs/index.d.ts +6 -0
  24. package/dist/fs/index.js +6 -0
  25. package/dist/fs/user-data-directories.d.ts +28 -0
  26. package/dist/fs/user-data-directories.js +53 -0
  27. package/dist/fs/verified-directory-move.d.ts +37 -0
  28. package/dist/fs/verified-directory-move.js +138 -0
  29. package/dist/fs/verified-file-relocation.d.ts +27 -0
  30. package/dist/fs/verified-file-relocation.js +115 -0
  31. package/dist/fs/workspace-directory.d.ts +19 -0
  32. package/dist/fs/workspace-directory.js +23 -0
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +2 -1
  35. package/dist/secrets/credential-sets/index.d.ts +5 -0
  36. package/dist/secrets/credential-sets/index.js +5 -0
  37. package/dist/secrets/credential-sets/vault.aad.d.ts +4 -0
  38. package/dist/secrets/credential-sets/vault.aad.js +8 -0
  39. package/dist/secrets/credential-sets/vault.meta.d.ts +10 -0
  40. package/dist/secrets/credential-sets/vault.meta.js +52 -0
  41. package/dist/secrets/credential-sets/vault.repo.memory.d.ts +51 -0
  42. package/dist/secrets/credential-sets/vault.repo.memory.js +148 -0
  43. package/dist/secrets/credential-sets/vault.service.d.ts +130 -0
  44. package/dist/secrets/credential-sets/vault.service.js +310 -0
  45. package/dist/secrets/credential-sets/vault.types.d.ts +150 -0
  46. package/dist/secrets/credential-sets/vault.types.js +28 -0
  47. package/dist/web/html-document.d.ts +70 -0
  48. package/dist/web/html-document.js +268 -0
  49. package/dist/web/html-resource.d.ts +15 -0
  50. package/dist/web/html-resource.js +12 -0
  51. package/dist/web/html-to-markdown.d.ts +11 -0
  52. package/dist/web/html-to-markdown.js +245 -0
  53. package/dist/web/index.d.ts +5 -0
  54. package/dist/web/index.js +5 -0
  55. package/dist/web/web-page.d.ts +157 -0
  56. package/dist/web/web-page.js +310 -0
  57. package/package.json +14 -3
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@
33
33
  */
34
34
  export type { CommandInvocation, CommandInvocationRequest } from "./command.js";
35
35
  export { createCommandInvocation, createPackageManagerInvocation } from "./command.js";
36
+ export { omitEnvironmentKeys } from "./child-environment.js";
36
37
  export type { ResolveSystemProxyEnvOptions, SystemProxyCommandRunner } from "./proxy-env.js";
37
38
  export { mergeProxyAwareEnv, parseMacosScutilProxyOutput, parseWindowsInternetSettingsProxyOutput, resolveSystemProxyEnv, } from "./proxy-env.js";
38
39
  export type { ProcessSnapshot, ProcessStampContract, ProcessStampField, ProcessStampShape, SpawnProcessRequest, StampedProcessMatchCriteria, StopProcessesResult, } from "./process.js";
@@ -48,7 +49,7 @@ export { wellKnownUserToolchainBins } from "./toolchain.js";
48
49
  export type { BufferedCommandResult } from "./shell.js";
49
50
  export { execCommandViaLoginShell, execFileBuffered } from "./shell.js";
50
51
  export type { AssetCache, AssetCacheOptions, AssetCacheResult } from "./asset-cache.js";
51
- export { AssetCacheError, assertSafePublicUrl, assetCacheKey, assetCacheRewriteUrl, createAssetCache, createValidatingLookup, isCacheableExternalUrl, } from "./asset-cache.js";
52
+ export { AssetCacheError, assertSafePublicUrl, assetCacheKey, assetCacheRewriteUrl, createAssetCache, createAssetCacheDispatcher, createValidatingLookup, isCacheableExternalUrl, } from "./asset-cache.js";
52
53
  export { expandHomePrefix, resolveProjectRelativePath } from "./home-expansion.js";
53
54
  export type { SandboxEnvConfig, SandboxRuntimeConfig, SandboxRuntimeRoots } from "./sandbox-env.js";
54
55
  export { applySandboxRuntimeEnv, ensureSandboxRuntimeDirs, isSandboxImportedProjectRootAllowed, isSandboxModeEnabled, resolveSandboxRuntimeConfig, resolveSandboxRuntimeConfigFromEnv, sandboxAgentProfilesConfigPath, sandboxImportAllowedRoots, sandboxImportedProjectRootUnavailableReason, } from "./sandbox-env.js";
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@
32
32
  * Filesystem/network storage and its SigV4 signing belong here, with no kernel SQL-port dependency.
33
33
  */
34
34
  export { createCommandInvocation, createPackageManagerInvocation } from "./command.js";
35
+ export { omitEnvironmentKeys } from "./child-environment.js";
35
36
  export { mergeProxyAwareEnv, parseMacosScutilProxyOutput, parseWindowsInternetSettingsProxyOutput, resolveSystemProxyEnv, } from "./proxy-env.js";
36
37
  export { collectProcessTreePids, createProcessStampArgs, isProcessAlive, listProcessSnapshots, matchesProcessStamp, matchesStampedProcess, readFlagValue, readProcessStamp, readProcessStampFromCommand, spawnBackgroundProcess, spawnLoggedProcess, stopProcesses, waitForProcessExit, } from "./process.js";
37
38
  export { atomicCopyFile, pathContains, readLogTail, removePathBestEffort } from "./fs.js";
@@ -39,7 +40,7 @@ export { waitForHttpOk } from "./http.js";
39
40
  export { FETCH_TIMEOUT_MS, FetchTimeoutError, fetchWithTimeout } from "./fetch-with-timeout.js";
40
41
  export { wellKnownUserToolchainBins } from "./toolchain.js";
41
42
  export { execCommandViaLoginShell, execFileBuffered } from "./shell.js";
42
- export { AssetCacheError, assertSafePublicUrl, assetCacheKey, assetCacheRewriteUrl, createAssetCache, createValidatingLookup, isCacheableExternalUrl, } from "./asset-cache.js";
43
+ export { AssetCacheError, assertSafePublicUrl, assetCacheKey, assetCacheRewriteUrl, createAssetCache, createAssetCacheDispatcher, createValidatingLookup, isCacheableExternalUrl, } from "./asset-cache.js";
43
44
  export { expandHomePrefix, resolveProjectRelativePath } from "./home-expansion.js";
44
45
  export { applySandboxRuntimeEnv, ensureSandboxRuntimeDirs, isSandboxImportedProjectRootAllowed, isSandboxModeEnabled, resolveSandboxRuntimeConfig, resolveSandboxRuntimeConfigFromEnv, sandboxAgentProfilesConfigPath, sandboxImportAllowedRoots, sandboxImportedProjectRootUnavailableReason, } from "./sandbox-env.js";
45
46
  export { resolveDaemonCliPath, resolveDaemonPluginPreviewsDir, resolveDaemonResourceDir, resolveDaemonResourceRoot, resolveDataDir, resolveProcessResourcesPath, } from "./resource-paths.js";
@@ -1,4 +1,9 @@
1
1
  export type { VendorCredentialSetRecord, VendorCredentialSetSummary, VendorCredentialSetRepoPort } from "./types.js";
2
2
  export { buildVendorCredentialAad } from "./aad.js";
3
3
  export { InMemoryVendorCredentialSetRepo } from "./repo.memory.js";
4
+ export * from "./vault.types.js";
5
+ export { buildVaultAad } from "./vault.aad.js";
6
+ export { createVaultMetaParsers, isCopyableKind } from "./vault.meta.js";
7
+ export { SecretsVaultService, type VaultServiceDeps, type VaultPutInput, type VaultCopyInput, type VaultCommitPreparedInput } from "./vault.service.js";
8
+ export { InMemoryVaultRepo } from "./vault.repo.memory.js";
4
9
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,8 @@
1
1
  export { buildVendorCredentialAad } from "./aad.js";
2
2
  export { InMemoryVendorCredentialSetRepo } from "./repo.memory.js";
3
+ export * from "./vault.types.js";
4
+ export { buildVaultAad } from "./vault.aad.js";
5
+ export { createVaultMetaParsers, isCopyableKind } from "./vault.meta.js";
6
+ export { SecretsVaultService } from "./vault.service.js";
7
+ export { InMemoryVaultRepo } from "./vault.repo.memory.js";
3
8
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type { VaultIdentity, VaultOptions } from "./vault.types.js";
2
+ /** JSON tuple encoding prevents colons or separators in host identifiers aliasing another row. */
3
+ export declare function buildVaultAad(input: VaultIdentity, _options?: VaultOptions): string;
4
+ //# sourceMappingURL=vault.aad.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { formatAad } from "../aad.js";
2
+ /** JSON tuple encoding prevents colons or separators in host identifiers aliasing another row. */
3
+ export function buildVaultAad(input, _options = {}) {
4
+ return formatAad({ kind: "secrets-vault", version: "v1", parts: [JSON.stringify([
5
+ input.workspaceId, input.id, input.kind, input.ownerKey, input.principalId, input.binding,
6
+ ])] });
7
+ }
8
+ //# sourceMappingURL=vault.aad.js.map
@@ -0,0 +1,10 @@
1
+ import { type VaultKind, type VaultMetaParsers, type VaultOptions } from "./vault.types.js";
2
+ /** Copy eligibility lives with the kind contracts; MCP rows require their server configuration. */
3
+ export declare const isCopyableKind: Readonly<Record<VaultKind, boolean>>;
4
+ /** Minimal structural parsers. Hosts inject catalog, URL/origin and safe-metadata policy here;
5
+ * secrets stay in payload JSON. Overrides own the complete shape, including merged patches.
6
+ */
7
+ export declare function createVaultMetaParsers(_required: VaultOptions, { parsers }?: {
8
+ parsers?: Partial<VaultMetaParsers>;
9
+ }): VaultMetaParsers;
10
+ //# sourceMappingURL=vault.meta.d.ts.map
@@ -0,0 +1,52 @@
1
+ import { VAULT_KINDS, VaultError } from "./vault.types.js";
2
+ /** Copy eligibility lives with the kind contracts; MCP rows require their server configuration. */
3
+ export const isCopyableKind = Object.freeze({
4
+ "site-assistant": true, "personal-execution": false, "media-provider": true, custom: true,
5
+ "publish-peer": true, "database-destination": true, vendor: true, "source-control": true,
6
+ "mcp-env": false, "mcp-auth": false,
7
+ });
8
+ const FIELDS = {
9
+ "site-assistant": { baseUrl: "string", model: "string" },
10
+ "personal-execution": { protocol: "string", baseUrl: "string", model: "string", maxTokens: "number" },
11
+ "media-provider": { baseUrl: "string", model: "string" },
12
+ custom: { category: "string", baseUrl: "string", additionalHosts: "strings", username: "string" },
13
+ "publish-peer": { baseUrl: "string", remoteWorkspaceId: "string" },
14
+ "database-destination": { description: "object", lastRun: "object" },
15
+ vendor: { accountLabel: "string" }, "source-control": { accountLabel: "string" },
16
+ "mcp-env": {}, "mcp-auth": {},
17
+ };
18
+ function isObject(value) {
19
+ return value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
20
+ }
21
+ function isJson(value) {
22
+ if (value === null || typeof value === "string" || typeof value === "boolean")
23
+ return true;
24
+ if (typeof value === "number")
25
+ return Number.isFinite(value);
26
+ if (Array.isArray(value))
27
+ return value.every(isJson);
28
+ return isObject(value) && Object.keys(value).every(key => !["__proto__", "constructor", "prototype"].includes(key) && isJson(value[key]));
29
+ }
30
+ /** Minimal structural parsers. Hosts inject catalog, URL/origin and safe-metadata policy here;
31
+ * secrets stay in payload JSON. Overrides own the complete shape, including merged patches.
32
+ */
33
+ export function createVaultMetaParsers(_required, { parsers = {} } = {}) {
34
+ return Object.fromEntries(VAULT_KINDS.map(kind => [kind, ({ meta }) => {
35
+ if (!isObject(meta) || !isJson(meta) || Object.keys(meta).some(key => !/^[A-Za-z0-9_]+$/.test(key)))
36
+ throw new VaultError({ code: "invalid-meta" });
37
+ if (parsers[kind]) {
38
+ const parsed = parsers[kind]({ meta: structuredClone(meta) });
39
+ if (!isObject(parsed) || !isJson(parsed) || Object.keys(parsed).some(key => !/^[A-Za-z0-9_]+$/.test(key)))
40
+ throw new VaultError({ code: "invalid-meta" });
41
+ return structuredClone(parsed);
42
+ }
43
+ for (const [key, value] of Object.entries(meta)) {
44
+ const field = Object.hasOwn(FIELDS[kind], key) ? FIELDS[kind][key] : undefined;
45
+ if (!field || (value !== null && !(field === "strings" ? Array.isArray(value) && value.every(v => typeof v === "string") :
46
+ field === "object" ? isObject(value) : typeof value === field)))
47
+ throw new VaultError({ code: "invalid-meta" });
48
+ }
49
+ return structuredClone(meta);
50
+ }]));
51
+ }
52
+ //# sourceMappingURL=vault.meta.js.map
@@ -0,0 +1,51 @@
1
+ import { type VaultBatchOp, type VaultBatchResult, type VaultDefaultQuery, type VaultSingletonQuery, type VaultMeta, type VaultOptions, type VaultQuery, type VaultRecord, type VaultRepoPort } from "./vault.types.js";
2
+ /** Single-turn mutations are atomic in JS; clones prevent callers bypassing CAS through aliases. */
3
+ export declare class InMemoryVaultRepo implements VaultRepoPort {
4
+ private rows;
5
+ private singletons;
6
+ constructor(_required: VaultOptions, _options?: VaultOptions);
7
+ private key;
8
+ find(input: {
9
+ workspaceId: string;
10
+ id: string;
11
+ }, _options?: VaultOptions): Promise<VaultRecord | null>;
12
+ private singletonKey;
13
+ /** Index all four optional scope combinations; copy sets so batch staging never mutates live indexes. */
14
+ private indexSingleton;
15
+ /** Indexed singleton selection; clone only the selected row, ignoring provider/default. */
16
+ findOne(input: VaultSingletonQuery, _options?: VaultOptions): Promise<VaultRecord | null>;
17
+ /** Stage through the same mutation owner, then publish once. No await can expose partial rows
18
+ * or let a concurrent writer be lost between snapshot and swap. O(n + ops*n) fixture work.
19
+ */
20
+ applyBatch({ ops }: {
21
+ ops: readonly VaultBatchOp[];
22
+ }, _options?: VaultOptions): Promise<VaultBatchResult[]>;
23
+ /** O(n) memory scan, n stored rows; clones only the default, never its siblings. */
24
+ findDefault(input: VaultDefaultQuery, _options?: VaultOptions): Promise<VaultRecord | null>;
25
+ list(input: VaultQuery, _options?: VaultOptions): Promise<VaultRecord[]>;
26
+ put(input: {
27
+ record: VaultRecord;
28
+ expectedRevision: number | null;
29
+ }, _options?: VaultOptions): Promise<VaultRecord>;
30
+ /** Synchronous mutation owner shared with batch staging; default moves never re-seal. */
31
+ private putRecord;
32
+ patchMeta(input: {
33
+ workspaceId: string;
34
+ id: string;
35
+ expectedRevision: number;
36
+ patch: VaultMeta;
37
+ updatedAt: string;
38
+ }, _options?: VaultOptions): Promise<VaultRecord>;
39
+ /** Clone merged metadata before mutating the staged/live map so failures leave it intact. */
40
+ private patchRecord;
41
+ delete(input: {
42
+ workspaceId: string;
43
+ id: string;
44
+ expectedRevision: number;
45
+ }, _options?: VaultOptions): Promise<boolean>;
46
+ /** Synchronous CAS delete shares promotion with provider moves and batch staging. */
47
+ private deleteRecord;
48
+ /** O(n) winner scan; promotion changes only default/revision, matching SQL. */
49
+ private promote;
50
+ }
51
+ //# sourceMappingURL=vault.repo.memory.d.ts.map
@@ -0,0 +1,148 @@
1
+ import { assertVaultReplacement, vaultGroupKey, VaultError } from "./vault.types.js";
2
+ /** Single-turn mutations are atomic in JS; clones prevent callers bypassing CAS through aliases. */
3
+ export class InMemoryVaultRepo {
4
+ rows = new Map();
5
+ singletons = new Map();
6
+ constructor(_required, _options = {}) { }
7
+ key(input) { return JSON.stringify([input.workspaceId, input.id]); }
8
+ async find(input, _options = {}) {
9
+ return structuredClone(this.rows.get(this.key(input)) ?? null);
10
+ }
11
+ singletonKey(input) {
12
+ return JSON.stringify([input.workspaceId, input.kind, input.ownerKey !== undefined, input.ownerKey ?? null,
13
+ input.principalId !== undefined, input.principalId ?? null]);
14
+ }
15
+ /** Index all four optional scope combinations; copy sets so batch staging never mutates live indexes. */
16
+ indexSingleton({ record, remove }, _options = {}) {
17
+ for (const ownerKey of [undefined, record.ownerKey])
18
+ for (const principalId of [undefined, record.principalId]) {
19
+ const scope = this.singletonKey({ workspaceId: record.workspaceId, kind: record.kind, ownerKey, principalId });
20
+ const ids = new Set(this.singletons.get(scope));
21
+ if (remove)
22
+ ids.delete(this.key(record));
23
+ else
24
+ ids.add(this.key(record));
25
+ if (ids.size)
26
+ this.singletons.set(scope, ids);
27
+ else
28
+ this.singletons.delete(scope);
29
+ }
30
+ }
31
+ /** Indexed singleton selection; clone only the selected row, ignoring provider/default. */
32
+ async findOne(input, _options = {}) {
33
+ const id = this.singletons.get(this.singletonKey(input))?.values().next().value;
34
+ return structuredClone(id === undefined ? null : this.rows.get(id));
35
+ }
36
+ /** Stage through the same mutation owner, then publish once. No await can expose partial rows
37
+ * or let a concurrent writer be lost between snapshot and swap. O(n + ops*n) fixture work.
38
+ */
39
+ async applyBatch({ ops }, _options = {}) {
40
+ const staged = new InMemoryVaultRepo({});
41
+ staged.rows = new Map(this.rows);
42
+ staged.singletons = new Map(this.singletons);
43
+ const results = ops.map(op => {
44
+ if ("put" in op)
45
+ return staged.putRecord(op.put);
46
+ if ("delete" in op)
47
+ return staged.deleteRecord(op.delete);
48
+ return staged.patchRecord(op.patchMeta);
49
+ });
50
+ this.rows = staged.rows;
51
+ this.singletons = staged.singletons;
52
+ return results;
53
+ }
54
+ /** O(n) memory scan, n stored rows; clones only the default, never its siblings. */
55
+ async findDefault(input, _options = {}) {
56
+ for (const row of this.rows.values()) {
57
+ if (row.isDefault && row.workspaceId === input.workspaceId && row.kind === input.kind && row.ownerKey === input.ownerKey &&
58
+ row.principalId === input.principalId && row.provider === input.provider)
59
+ return structuredClone(row);
60
+ }
61
+ return null;
62
+ }
63
+ async list(input, _options = {}) {
64
+ return structuredClone([...this.rows.values()].filter(row => row.workspaceId === input.workspaceId && row.kind === input.kind &&
65
+ row.ownerKey === input.ownerKey && row.principalId === input.principalId && (input.provider === undefined || row.provider === input.provider))
66
+ .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
67
+ }
68
+ async put(input, _options = {}) {
69
+ return this.putRecord(input);
70
+ }
71
+ /** Synchronous mutation owner shared with batch staging; default moves never re-seal. */
72
+ putRecord(input, _options = {}) {
73
+ const { record } = input;
74
+ const key = this.key(record);
75
+ const existing = this.rows.get(key) ?? null;
76
+ assertVaultReplacement({ ...input, existing });
77
+ const groupKey = vaultGroupKey({ record });
78
+ let hasGroup = false;
79
+ for (const row of this.rows.values())
80
+ if (vaultGroupKey({ record: row }) === groupKey) {
81
+ hasGroup = true;
82
+ if (row.id !== record.id && row.label === record.label)
83
+ throw new VaultError({ code: "conflict" });
84
+ }
85
+ const saved = structuredClone({ ...record, isDefault: record.isDefault || !hasGroup });
86
+ // Clear first, matching a SQL partial UNIQUE index; no transient duplicate default is required.
87
+ if (saved.isDefault)
88
+ for (const row of this.rows.values()) {
89
+ if (row.id !== saved.id && row.isDefault && vaultGroupKey({ record: row }) === groupKey) {
90
+ this.rows.set(this.key(row), { ...row, isDefault: false, revision: row.revision + 1 });
91
+ }
92
+ }
93
+ this.rows.set(key, saved);
94
+ if (!existing)
95
+ this.indexSingleton({ record: saved, remove: false });
96
+ if (existing?.isDefault && vaultGroupKey({ record: existing }) !== groupKey)
97
+ this.promote({ record: existing });
98
+ return structuredClone(saved);
99
+ }
100
+ async patchMeta(input, _options = {}) {
101
+ return this.patchRecord(input);
102
+ }
103
+ /** Clone merged metadata before mutating the staged/live map so failures leave it intact. */
104
+ patchRecord(input, _options = {}) {
105
+ const key = this.key(input);
106
+ const existing = this.rows.get(key);
107
+ if (existing?.revision !== input.expectedRevision)
108
+ throw new VaultError({ code: "conflict" });
109
+ const saved = { ...existing, meta: structuredClone({ ...existing.meta, ...input.patch }), updatedAt: input.updatedAt, revision: existing.revision + 1 };
110
+ this.rows.set(key, saved);
111
+ return structuredClone(saved);
112
+ }
113
+ async delete(input, _options = {}) {
114
+ return this.deleteRecord(input);
115
+ }
116
+ /** Synchronous CAS delete shares promotion with provider moves and batch staging. */
117
+ deleteRecord(input, _options = {}) {
118
+ const key = this.key(input);
119
+ const existing = this.rows.get(key);
120
+ if (!existing)
121
+ return false;
122
+ if (existing.revision !== input.expectedRevision)
123
+ throw new VaultError({ code: "conflict" });
124
+ this.rows.delete(key);
125
+ this.indexSingleton({ record: existing, remove: true });
126
+ if (existing.isDefault)
127
+ this.promote({ record: existing });
128
+ return true;
129
+ }
130
+ /** O(n) winner scan; promotion changes only default/revision, matching SQL. */
131
+ promote({ record }, _options = {}) {
132
+ const groupKey = vaultGroupKey({ record });
133
+ let promoted;
134
+ // SQL BINARY/C compares UTF-8 bytes; JS's UTF-16 order differs for supplementary IDs.
135
+ for (const row of this.rows.values()) {
136
+ if (vaultGroupKey({ record: row }) === groupKey && (!promoted || row.updatedAt > promoted.updatedAt ||
137
+ (row.updatedAt === promoted.updatedAt && Buffer.compare(Buffer.from(row.id), Buffer.from(promoted.id)) > 0))) {
138
+ promoted = { id: row.id, updatedAt: row.updatedAt };
139
+ }
140
+ }
141
+ if (promoted) {
142
+ const promotedKey = this.key({ workspaceId: record.workspaceId, id: promoted.id });
143
+ const row = this.rows.get(promotedKey);
144
+ this.rows.set(promotedKey, { ...row, isDefault: true, revision: row.revision + 1 });
145
+ }
146
+ }
147
+ }
148
+ //# sourceMappingURL=vault.repo.memory.js.map
@@ -0,0 +1,130 @@
1
+ import type { KeyringPort, SecretSealerPort } from "../ports.js";
2
+ import { type VaultContext, type VaultIdentity, type VaultMeta, type VaultMetaParsers, type VaultCopyResult, type VaultDefaultQuery, type VaultOptions, type VaultQuery, type VaultRecord, type VaultRepoPort, type VaultSummary } from "./vault.types.js";
3
+ export interface VaultServiceDeps {
4
+ repo: VaultRepoPort;
5
+ keyring: KeyringPort;
6
+ sealer: SecretSealerPort;
7
+ metaParsers: VaultMetaParsers;
8
+ /** Host policy must throw on denial. No permissive default is provided. */
9
+ authorize: (input: {
10
+ action: "put" | "get" | "list" | "rotate" | "delete";
11
+ context: VaultContext | VaultIdentity | VaultQuery;
12
+ }) => Promise<void>;
13
+ now: (input: VaultOptions) => string;
14
+ newId: (input: VaultOptions) => string;
15
+ }
16
+ export interface VaultPutInput extends VaultContext {
17
+ /** Omit for creation with an injected ID. Replacement requires id and expectedRevision. */
18
+ id?: string;
19
+ expectedRevision: number | null;
20
+ provider: string | null;
21
+ label: string;
22
+ meta: unknown;
23
+ /** null clears; omitted on replacement preserves the envelope/hint without opening or sealing.
24
+ * Creation requires a payload (which may be null). Payloads are always sealed JSON.
25
+ */
26
+ payload?: VaultMeta | null | undefined;
27
+ /** Omitted preserves same-group default status; a provider move starts with false and the
28
+ * repository applies its first-row rule. Explicit true selects the destination default.
29
+ */
30
+ isDefault?: boolean;
31
+ /** A host-selected token, never a URI or serialized connection object. */
32
+ hintToken?: string;
33
+ }
34
+ /** Prepared records contain envelopes and stay private to the host, never a public read model.
35
+ * Expected revisions are mandatory for every ID; null means create-only. Deletes carry full
36
+ * expected ownership. Default side effects follow repository operation order (records, deletes).
37
+ */
38
+ export interface VaultCommitPreparedInput {
39
+ records: readonly VaultRecord[];
40
+ deletes: readonly VaultIdentity[];
41
+ expectedRevisions: Readonly<Record<string, number | null>>;
42
+ }
43
+ export interface VaultCopyInput {
44
+ /** This service owns the source repository, authorization and keyring-backed sealer. */
45
+ source: SecretsVaultService;
46
+ sourceContext: VaultContext;
47
+ targetContext: VaultContext;
48
+ ids: readonly string[];
49
+ overwrite: boolean;
50
+ /** Optional selection-time guards; otherwise the revisions read by this operation are used. */
51
+ sourceRevisions?: Readonly<Record<string, number>>;
52
+ targetRevisions?: Readonly<Record<string, number | null>>;
53
+ }
54
+ /** Shared vault lifecycle. Policy/catalogs are injected; persistence and encryption are ports.
55
+ * Expected context is checked before opening. Normal reads have no legacy-AAD fallback.
56
+ * Metadata reads never need keys, and every merged patch is parsed before CAS persistence.
57
+ */
58
+ export declare class SecretsVaultService {
59
+ private readonly deps;
60
+ constructor(deps: VaultServiceDeps, _options?: VaultOptions);
61
+ private authorize;
62
+ private parse;
63
+ private find;
64
+ private seal;
65
+ private open;
66
+ /** Create/replace configuration and a sealed JSON payload. Throws on policy, validation or CAS failure. */
67
+ put(input: VaultPutInput, _options?: VaultOptions): Promise<VaultSummary>;
68
+ /** Authorize, validate and seal before the host enters its transaction, without persisting.
69
+ * Replacements retain identity/createdAt; omitted payload preserves the existing envelope.
70
+ * Commit must CAS again because preparation does not reserve a revision or a default.
71
+ * @example const record = await vault.prepare(input); // pass to commitPrepared inside the host transaction
72
+ */
73
+ prepare(input: VaultPutInput, _options?: VaultOptions): Promise<VaultRecord>;
74
+ /** Reauthorize prepared rows, validate ownership/metadata, then commit through the batch owner.
75
+ * No plaintext is opened or sealed. Missing capability/revisions and duplicate identities fail
76
+ * before writes; stale CAS or a later operation failure rolls back the whole repository batch.
77
+ * @example await vault.commitPrepared({ records: [record], deletes: [], expectedRevisions: { [record.id]: null } });
78
+ */
79
+ commitPrepared(input: VaultCommitPreparedInput, _options?: VaultOptions): Promise<VaultSummary[]>;
80
+ /** Authorized runtime read; returns plaintext only to the caller that supplied exact ownership. */
81
+ get(input: VaultIdentity, _options?: VaultOptions): Promise<{
82
+ summary: VaultSummary;
83
+ payload: VaultMeta | null;
84
+ } | null>;
85
+ /** Singleton runtime read using one provider-independent lookup. Full expected ownership and
86
+ * binding are required before policy/decryption; repo callers may use a broader selection scope.
87
+ * @example await vault.findOne({ workspaceId, kind: "site-assistant", ownerKey: "assistant", principalId: null, binding: null });
88
+ */
89
+ findOne(input: VaultContext, _options?: VaultOptions): Promise<{
90
+ summary: VaultSummary;
91
+ payload: VaultMeta | null;
92
+ } | null>;
93
+ /** Authorized default read using one exact-group query; ownership is checked before opening.
94
+ * @throws On policy, context, metadata or authentication failure.
95
+ * @complexity One indexed row lookup, then O(payload size) decryption.
96
+ */
97
+ findDefault(input: VaultDefaultQuery & {
98
+ binding: string | null;
99
+ }, _options?: VaultOptions): Promise<{
100
+ summary: VaultSummary;
101
+ payload: VaultMeta | null;
102
+ } | null>;
103
+ /** Copy selected rows into this service without exposing plaintext or envelopes. Each ID is
104
+ * attempted once; failures are isolated and raw adapter/policy/crypto errors never escape.
105
+ * Source reads are plain SELECTs; this operation never upgrades or writes the source.
106
+ * @throws VaultError for invalid scopes. Per-row failures use fixed reason codes.
107
+ * @complexity O(n + payload bytes), n unique IDs; sequential bounded row reads and CAS writes.
108
+ */
109
+ copyToSite(input: VaultCopyInput, _options?: VaultOptions): Promise<VaultCopyResult[]>;
110
+ /** One transfer reuses both services' parsing/open/seal and the repository's default/CAS owner.
111
+ * @complexity Constant row queries plus O(payload size); no group read or source transaction.
112
+ */
113
+ private copyRow;
114
+ /** Human-managed scoped inventory. No envelope or plaintext is opened or returned. */
115
+ list(input: VaultQuery, _options?: VaultOptions): Promise<VaultSummary[]>;
116
+ /** Merge and validate complete metadata, then target only changed JSON keys with revision CAS. */
117
+ patchMeta(input: VaultIdentity & {
118
+ expectedRevision: number;
119
+ patch: VaultMeta;
120
+ }, _options?: VaultOptions): Promise<VaultSummary>;
121
+ /** Re-seal using the active key; concurrent replacement wins through CAS. Metadata-only rows persist. */
122
+ rotate(input: VaultIdentity & {
123
+ expectedRevision: number;
124
+ }, _options?: VaultOptions): Promise<VaultSummary>;
125
+ /** Idempotent for missing rows; existing rows require matching context and revision. */
126
+ delete(input: VaultIdentity & {
127
+ expectedRevision: number;
128
+ }, _options?: VaultOptions): Promise<boolean>;
129
+ }
130
+ //# sourceMappingURL=vault.service.d.ts.map