@jini-ai/platform 0.5.3 → 0.5.4
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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/secrets/credential-sets/index.d.ts +1 -3
- package/dist/secrets/credential-sets/index.js +0 -2
- package/dist/secrets/credential-sets/vault.service.d.ts +3 -18
- package/dist/secrets/credential-sets/vault.service.js +2 -39
- package/dist/secrets/credential-sets/vault.types.d.ts +2 -1
- package/dist/secrets/testing.d.ts +0 -1
- package/dist/secrets/testing.js +0 -1
- package/package.json +1 -1
- package/dist/secrets/credential-sets/aad.d.ts +0 -7
- package/dist/secrets/credential-sets/aad.js +0 -7
- package/dist/secrets/credential-sets/repo.memory.d.ts +0 -44
- package/dist/secrets/credential-sets/repo.memory.js +0 -80
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @jini-ai/platform
|
|
2
2
|
|
|
3
|
+
## 0.5.4 — 2026-10-12
|
|
4
|
+
|
|
5
|
+
- Removed unused exports: `SecretsVaultService.commitPrepared`, `VaultCommitPreparedInput`, `VaultCopyInput.targetRevisions`, the pre-vault `InMemoryVendorCredentialSetRepo` and `buildVendorCredentialAad`. No known consumer; prepared records are committed through the vault repository's `put`/`applyBatch`.
|
|
6
|
+
|
|
3
7
|
## 0.5.2
|
|
4
8
|
|
|
5
9
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ New factories/functions/methods accept a required object and, where needed, a se
|
|
|
140
140
|
object. No-input operations take `{}`: `createNodeGuardedHttpPorts({})`,
|
|
141
141
|
`createNodeGuardedReaderFilesystem({})`, `createNodeDurableJsonPorts({})`,
|
|
142
142
|
`new FetchHttpTransportAdapter({})`, `new InMemoryTrashRepo({})`,
|
|
143
|
-
`
|
|
143
|
+
`keyring.activeKey({})`,
|
|
144
144
|
`mailer.capabilities({})`, `clock.nowMs({})`, `clock.nowIso({})`, `random.token({})`,
|
|
145
145
|
and `file.read({})`. SMTP ports receive `sendMail({ mail })`; `NodemailerPort` exposes the
|
|
146
146
|
vendor-shaped `NativeSmtpTransport` so the factory adapts the payload once. Policy errors use
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export type { VendorCredentialSetRecord, VendorCredentialSetSummary, VendorCredentialSetRepoPort } from "./types.js";
|
|
2
|
-
export { buildVendorCredentialAad } from "./aad.js";
|
|
3
|
-
export { InMemoryVendorCredentialSetRepo } from "./repo.memory.js";
|
|
4
2
|
export * from "./vault.types.js";
|
|
5
3
|
export { buildVaultAad } from "./vault.aad.js";
|
|
6
4
|
export { createVaultMetaParsers, isCopyableKind } from "./vault.meta.js";
|
|
7
|
-
export { SecretsVaultService, type VaultServiceDeps, type VaultPutInput, type VaultCopyInput
|
|
5
|
+
export { SecretsVaultService, type VaultServiceDeps, type VaultPutInput, type VaultCopyInput } from "./vault.service.js";
|
|
8
6
|
export { InMemoryVaultRepo } from "./vault.repo.memory.js";
|
|
9
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { buildVendorCredentialAad } from "./aad.js";
|
|
2
|
-
export { InMemoryVendorCredentialSetRepo } from "./repo.memory.js";
|
|
3
1
|
export * from "./vault.types.js";
|
|
4
2
|
export { buildVaultAad } from "./vault.aad.js";
|
|
5
3
|
export { createVaultMetaParsers, isCopyableKind } from "./vault.meta.js";
|
|
@@ -31,15 +31,6 @@ export interface VaultPutInput extends VaultContext {
|
|
|
31
31
|
/** A host-selected token, never a URI or serialized connection object. */
|
|
32
32
|
hintToken?: string;
|
|
33
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
34
|
export interface VaultCopyInput {
|
|
44
35
|
/** This service owns the source repository, authorization and keyring-backed sealer. */
|
|
45
36
|
source: SecretsVaultService;
|
|
@@ -47,9 +38,8 @@ export interface VaultCopyInput {
|
|
|
47
38
|
targetContext: VaultContext;
|
|
48
39
|
ids: readonly string[];
|
|
49
40
|
overwrite: boolean;
|
|
50
|
-
/** Optional selection-time
|
|
41
|
+
/** Optional selection-time guard; otherwise the revisions read by this operation are used. */
|
|
51
42
|
sourceRevisions?: Readonly<Record<string, number>>;
|
|
52
|
-
targetRevisions?: Readonly<Record<string, number | null>>;
|
|
53
43
|
}
|
|
54
44
|
/** Shared vault lifecycle. Policy/catalogs are injected; persistence and encryption are ports.
|
|
55
45
|
* Expected context is checked before opening. Normal reads have no legacy-AAD fallback.
|
|
@@ -68,15 +58,10 @@ export declare class SecretsVaultService {
|
|
|
68
58
|
/** Authorize, validate and seal before the host enters its transaction, without persisting.
|
|
69
59
|
* Replacements retain identity/createdAt; omitted payload preserves the existing envelope.
|
|
70
60
|
* Commit must CAS again because preparation does not reserve a revision or a default.
|
|
71
|
-
*
|
|
61
|
+
* Prepared records contain envelopes and stay private to the host, never a public read model.
|
|
62
|
+
* @example const record = await vault.prepare(input); await repo.put({ record, expectedRevision: input.expectedRevision }); // inside the host transaction
|
|
72
63
|
*/
|
|
73
64
|
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
65
|
/** Authorized runtime read; returns plaintext only to the caller that supplied exact ownership. */
|
|
81
66
|
get(input: VaultIdentity, _options?: VaultOptions): Promise<{
|
|
82
67
|
summary: VaultSummary;
|
|
@@ -81,7 +81,8 @@ export class SecretsVaultService {
|
|
|
81
81
|
/** Authorize, validate and seal before the host enters its transaction, without persisting.
|
|
82
82
|
* Replacements retain identity/createdAt; omitted payload preserves the existing envelope.
|
|
83
83
|
* Commit must CAS again because preparation does not reserve a revision or a default.
|
|
84
|
-
*
|
|
84
|
+
* Prepared records contain envelopes and stay private to the host, never a public read model.
|
|
85
|
+
* @example const record = await vault.prepare(input); await repo.put({ record, expectedRevision: input.expectedRevision }); // inside the host transaction
|
|
85
86
|
*/
|
|
86
87
|
async prepare(input, _options = {}) {
|
|
87
88
|
assertContext(input);
|
|
@@ -109,42 +110,6 @@ export class SecretsVaultService {
|
|
|
109
110
|
createdAt: existing?.createdAt ?? now, updatedAt: now };
|
|
110
111
|
return record;
|
|
111
112
|
}
|
|
112
|
-
/** Reauthorize prepared rows, validate ownership/metadata, then commit through the batch owner.
|
|
113
|
-
* No plaintext is opened or sealed. Missing capability/revisions and duplicate identities fail
|
|
114
|
-
* before writes; stale CAS or a later operation failure rolls back the whole repository batch.
|
|
115
|
-
* @example await vault.commitPrepared({ records: [record], deletes: [], expectedRevisions: { [record.id]: null } });
|
|
116
|
-
*/
|
|
117
|
-
async commitPrepared(input, _options = {}) {
|
|
118
|
-
if (!this.deps.repo.applyBatch)
|
|
119
|
-
throw new VaultError({ code: "invalid-record" });
|
|
120
|
-
const { records, deletes, expectedRevisions } = structuredClone(input);
|
|
121
|
-
const identities = new Set();
|
|
122
|
-
const ops = [];
|
|
123
|
-
const entries = [...records.map(record => ({ put: record })), ...deletes.map(record => ({ delete: record }))];
|
|
124
|
-
for (const entry of entries) {
|
|
125
|
-
const record = "put" in entry ? entry.put : entry.delete;
|
|
126
|
-
assertContext(record);
|
|
127
|
-
const key = JSON.stringify([record.workspaceId, record.id]);
|
|
128
|
-
if (identities.has(key) || !Object.hasOwn(expectedRevisions, record.id))
|
|
129
|
-
throw new VaultError({ code: "invalid-record" });
|
|
130
|
-
identities.add(key);
|
|
131
|
-
const expectedRevision = expectedRevisions[record.id];
|
|
132
|
-
const putting = "put" in entry;
|
|
133
|
-
await this.authorize({ action: putting ? "put" : "delete", context: record });
|
|
134
|
-
const existing = await this.find(record);
|
|
135
|
-
if (expectedRevision === null ? existing !== null : existing?.revision !== expectedRevision)
|
|
136
|
-
throw new VaultError({ code: "conflict" });
|
|
137
|
-
if ("put" in entry)
|
|
138
|
-
ops.push({ put: { record: this.parse(entry.put), expectedRevision } });
|
|
139
|
-
else {
|
|
140
|
-
if (expectedRevision === null)
|
|
141
|
-
throw new VaultError({ code: "invalid-record" });
|
|
142
|
-
ops.push({ delete: { workspaceId: record.workspaceId, id: record.id, expectedRevision } });
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const results = await this.deps.repo.applyBatch({ ops });
|
|
146
|
-
return results.filter((result) => typeof result !== "boolean").map(summary);
|
|
147
|
-
}
|
|
148
113
|
/** Authorized runtime read; returns plaintext only to the caller that supplied exact ownership. */
|
|
149
114
|
async get(input, _options = {}) {
|
|
150
115
|
assertContext(input);
|
|
@@ -240,8 +205,6 @@ export class SecretsVaultService {
|
|
|
240
205
|
if (existing && existing.provider !== record.provider)
|
|
241
206
|
throw new VaultError({ code: "context" });
|
|
242
207
|
const expectedRevision = existing?.revision ?? null;
|
|
243
|
-
if (input.targetRevisions && Object.hasOwn(input.targetRevisions, id) && input.targetRevisions[id] !== expectedRevision)
|
|
244
|
-
throw new VaultError({ code: "conflict" });
|
|
245
208
|
const now = this.deps.now({});
|
|
246
209
|
const candidate = this.parse({ ...record, ...targetIdentity, isDefault: existing?.isDefault ?? false,
|
|
247
210
|
revision: (expectedRevision ?? 0) + 1, createdAt: existing?.createdAt ?? now, updatedAt: now });
|
|
@@ -111,7 +111,8 @@ export interface VaultRepoPort {
|
|
|
111
111
|
findDefault?(input: VaultDefaultQuery, options?: VaultOptions): Promise<VaultRecord | null>;
|
|
112
112
|
/** Optional additions keep older injected ports source-compatible; built-in adapters implement both. */
|
|
113
113
|
findOne?(input: VaultSingletonQuery, options?: VaultOptions): Promise<VaultRecord | null>;
|
|
114
|
-
/** All operations commit together or throw with no published changes.
|
|
114
|
+
/** All operations commit together or throw with no published changes. Default side effects
|
|
115
|
+
* follow operation order; a delete carries full expected ownership.
|
|
115
116
|
* SQL joins the host transaction: callers must propagate failures to roll that transaction back.
|
|
116
117
|
* @example await repo.applyBatch!({ ops: [{ put: { record, expectedRevision: null } }] });
|
|
117
118
|
*/
|
package/dist/secrets/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/platform",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Generic OS/platform primitives: process exec and lifecycle, filesystem containment, HTTP readiness polling, toolchain discovery, blob storage, AWS signing, and an SSRF-safe asset cache.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { formatAad } from "../aad.js";
|
|
2
|
-
const AAD_VERSION = "v1";
|
|
3
|
-
/** Stable v1 AAD binding workspace, vendor, and credential identity. */
|
|
4
|
-
export function buildVendorCredentialAad(input) {
|
|
5
|
-
return formatAad({ kind: "vendor-credential-set", version: AAD_VERSION, parts: [input.workspaceId, input.vendorId, input.id] });
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=aad.js.map
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { VendorCredentialSetRecord, VendorCredentialSetRepoPort } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* @file `VendorCredentialSetRepoPort`'s in-memory adapter — the ADR-006 rule-of-two test double,
|
|
4
|
-
* mirroring `../deployments/publish-credentials/repo.memory.ts`'s shape byte-for-byte with
|
|
5
|
-
* `providerId` swapped for `vendorId`. See that file's own header for the full reasoning (row
|
|
6
|
-
* keying, the enforced UNIQUE constraint matching better-sqlite3's own error wording, why no
|
|
7
|
-
* explicit lock is needed for the `isDefault` group invariant in a single-threaded JS process).
|
|
8
|
-
*/
|
|
9
|
-
export declare class InMemoryVendorCredentialSetRepo implements VendorCredentialSetRepoPort {
|
|
10
|
-
constructor(_required: Record<string, never>, _optional?: Record<string, never>);
|
|
11
|
-
private readonly rows;
|
|
12
|
-
private static rowKey;
|
|
13
|
-
private assertLabelAvailable;
|
|
14
|
-
private clearOtherDefaults;
|
|
15
|
-
insert(record: VendorCredentialSetRecord, _optional?: Record<string, never>): Promise<void>;
|
|
16
|
-
update(record: VendorCredentialSetRecord, _optional?: Record<string, never>): Promise<void>;
|
|
17
|
-
findById(input: {
|
|
18
|
-
workspaceId: string;
|
|
19
|
-
id: string;
|
|
20
|
-
}, _optional?: Record<string, never>): Promise<VendorCredentialSetRecord | null>;
|
|
21
|
-
findDefaultByVendor(input: {
|
|
22
|
-
workspaceId: string;
|
|
23
|
-
vendorId: string;
|
|
24
|
-
}, _optional?: Record<string, never>): Promise<VendorCredentialSetRecord | null>;
|
|
25
|
-
listByVendor(input: {
|
|
26
|
-
workspaceId: string;
|
|
27
|
-
vendorId: string;
|
|
28
|
-
}, _optional?: Record<string, never>): Promise<VendorCredentialSetRecord[]>;
|
|
29
|
-
listByWorkspace(input: {
|
|
30
|
-
workspaceId: string;
|
|
31
|
-
}, _optional?: Record<string, never>): Promise<VendorCredentialSetRecord[]>;
|
|
32
|
-
delete(input: {
|
|
33
|
-
workspaceId: string;
|
|
34
|
-
id: string;
|
|
35
|
-
}, _optional?: Record<string, never>): Promise<void>;
|
|
36
|
-
/** Mirrors the SQLite adapter's targeted single-column write — see
|
|
37
|
-
* `VendorCredentialSetRepoPort.updateAccountLabel`'s own doc. No-op if the row vanished. */
|
|
38
|
-
updateAccountLabel(input: {
|
|
39
|
-
workspaceId: string;
|
|
40
|
-
id: string;
|
|
41
|
-
accountLabel: string;
|
|
42
|
-
}, _optional?: Record<string, never>): Promise<void>;
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=repo.memory.d.ts.map
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file `VendorCredentialSetRepoPort`'s in-memory adapter — the ADR-006 rule-of-two test double,
|
|
3
|
-
* mirroring `../deployments/publish-credentials/repo.memory.ts`'s shape byte-for-byte with
|
|
4
|
-
* `providerId` swapped for `vendorId`. See that file's own header for the full reasoning (row
|
|
5
|
-
* keying, the enforced UNIQUE constraint matching better-sqlite3's own error wording, why no
|
|
6
|
-
* explicit lock is needed for the `isDefault` group invariant in a single-threaded JS process).
|
|
7
|
-
*/
|
|
8
|
-
export class InMemoryVendorCredentialSetRepo {
|
|
9
|
-
constructor(_required, _optional = {}) { }
|
|
10
|
-
rows = new Map();
|
|
11
|
-
static rowKey(workspaceId, id) {
|
|
12
|
-
return `${workspaceId}::${id}`;
|
|
13
|
-
}
|
|
14
|
-
assertLabelAvailable(workspaceId, vendorId, label, excludingId) {
|
|
15
|
-
for (const row of this.rows.values()) {
|
|
16
|
-
if (row.workspaceId !== workspaceId || row.vendorId !== vendorId || row.label !== label)
|
|
17
|
-
continue;
|
|
18
|
-
if (excludingId !== undefined && row.id === excludingId)
|
|
19
|
-
continue;
|
|
20
|
-
throw new Error(`UNIQUE constraint failed: vendor_credential_sets.workspace_id, vendor_credential_sets.vendor_id, vendor_credential_sets.label`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
clearOtherDefaults(workspaceId, vendorId, keepId) {
|
|
24
|
-
for (const [key, row] of this.rows) {
|
|
25
|
-
if (row.workspaceId !== workspaceId || row.vendorId !== vendorId || row.id === keepId || !row.isDefault)
|
|
26
|
-
continue;
|
|
27
|
-
this.rows.set(key, { ...row, isDefault: false });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
async insert(record, _optional = {}) {
|
|
31
|
-
this.assertLabelAvailable(record.workspaceId, record.vendorId, record.label);
|
|
32
|
-
this.rows.set(InMemoryVendorCredentialSetRepo.rowKey(record.workspaceId, record.id), { ...record });
|
|
33
|
-
if (record.isDefault)
|
|
34
|
-
this.clearOtherDefaults(record.workspaceId, record.vendorId, record.id);
|
|
35
|
-
}
|
|
36
|
-
async update(record, _optional = {}) {
|
|
37
|
-
this.assertLabelAvailable(record.workspaceId, record.vendorId, record.label, record.id);
|
|
38
|
-
this.rows.set(InMemoryVendorCredentialSetRepo.rowKey(record.workspaceId, record.id), { ...record });
|
|
39
|
-
if (record.isDefault)
|
|
40
|
-
this.clearOtherDefaults(record.workspaceId, record.vendorId, record.id);
|
|
41
|
-
}
|
|
42
|
-
async findById(input, _optional = {}) {
|
|
43
|
-
return this.rows.get(InMemoryVendorCredentialSetRepo.rowKey(input.workspaceId, input.id)) ?? null;
|
|
44
|
-
}
|
|
45
|
-
async findDefaultByVendor(input, _optional = {}) {
|
|
46
|
-
for (const row of this.rows.values()) {
|
|
47
|
-
if (row.workspaceId === input.workspaceId && row.vendorId === input.vendorId && row.isDefault)
|
|
48
|
-
return row;
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
async listByVendor(input, _optional = {}) {
|
|
53
|
-
return [...this.rows.values()].filter((row) => row.workspaceId === input.workspaceId && row.vendorId === input.vendorId);
|
|
54
|
-
}
|
|
55
|
-
async listByWorkspace(input, _optional = {}) {
|
|
56
|
-
return [...this.rows.values()].filter((row) => row.workspaceId === input.workspaceId);
|
|
57
|
-
}
|
|
58
|
-
async delete(input, _optional = {}) {
|
|
59
|
-
const key = InMemoryVendorCredentialSetRepo.rowKey(input.workspaceId, input.id);
|
|
60
|
-
const removed = this.rows.get(key);
|
|
61
|
-
this.rows.delete(key);
|
|
62
|
-
if (!removed?.isDefault)
|
|
63
|
-
return;
|
|
64
|
-
const remaining = [...this.rows.values()].filter((row) => row.workspaceId === removed.workspaceId && row.vendorId === removed.vendorId);
|
|
65
|
-
if (remaining.length === 0)
|
|
66
|
-
return;
|
|
67
|
-
const promoted = remaining.reduce((latest, row) => (row.updatedAt > latest.updatedAt ? row : latest));
|
|
68
|
-
this.rows.set(InMemoryVendorCredentialSetRepo.rowKey(promoted.workspaceId, promoted.id), { ...promoted, isDefault: true });
|
|
69
|
-
}
|
|
70
|
-
/** Mirrors the SQLite adapter's targeted single-column write — see
|
|
71
|
-
* `VendorCredentialSetRepoPort.updateAccountLabel`'s own doc. No-op if the row vanished. */
|
|
72
|
-
async updateAccountLabel(input, _optional = {}) {
|
|
73
|
-
const key = InMemoryVendorCredentialSetRepo.rowKey(input.workspaceId, input.id);
|
|
74
|
-
const existing = this.rows.get(key);
|
|
75
|
-
if (!existing)
|
|
76
|
-
return;
|
|
77
|
-
this.rows.set(key, { ...existing, accountLabel: input.accountLabel });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
//# sourceMappingURL=repo.memory.js.map
|