@noy-db/hub 0.2.0-pre.21 → 0.2.0-pre.23
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/attestation/index.d.cts +1 -1
- package/dist/attestation/index.d.ts +1 -1
- package/dist/blobs/index.d.cts +3 -3
- package/dist/blobs/index.d.ts +3 -3
- package/dist/bundle/index.cjs +21664 -21247
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +3 -3
- package/dist/bundle/index.d.ts +3 -3
- package/dist/bundle/index.js +1 -1
- package/dist/{chunk-7PH4OPBZ.js → chunk-P65YMN5V.js} +388 -5
- package/dist/chunk-P65YMN5V.js.map +1 -0
- package/dist/consent/index.d.cts +2 -2
- package/dist/consent/index.d.ts +2 -2
- package/dist/derivations/index.d.cts +3 -3
- package/dist/derivations/index.d.ts +3 -3
- package/dist/{dev-unlock-CY0HIZA0.d.cts → dev-unlock-Bw7iBD1D.d.cts} +1 -1
- package/dist/{dev-unlock-CpKSkl2c.d.ts → dev-unlock-DzDzLTdZ.d.ts} +1 -1
- package/dist/guards/index.d.cts +3 -3
- package/dist/guards/index.d.ts +3 -3
- package/dist/{hash-BSd0-_L8.d.cts → hash-C52X_-m5.d.cts} +1 -1
- package/dist/{hash-BnBQx39y.d.ts → hash-DepR-xVc.d.ts} +1 -1
- package/dist/history/index.d.cts +3 -3
- package/dist/history/index.d.ts +3 -3
- package/dist/i18n/index.d.cts +2 -2
- package/dist/i18n/index.d.ts +2 -2
- package/dist/index.cjs +396 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/materialized-views/index.d.cts +3 -3
- package/dist/materialized-views/index.d.ts +3 -3
- package/dist/{mime-magic-BnJCGJzB.d.cts → mime-magic-Cxf9B_Dm.d.cts} +1 -1
- package/dist/{mime-magic-CjSyakO4.d.ts → mime-magic-Dejetix_.d.ts} +1 -1
- package/dist/{noydb-ZZCRF6TE.js → noydb-VGR2HLDB.js} +3 -2
- package/dist/overlay-views/index.d.cts +3 -3
- package/dist/overlay-views/index.d.ts +3 -3
- package/dist/periods/index.d.cts +2 -2
- package/dist/periods/index.d.ts +2 -2
- package/dist/session/index.d.cts +3 -3
- package/dist/session/index.d.ts +3 -3
- package/dist/shadow/index.d.cts +2 -2
- package/dist/shadow/index.d.ts +2 -2
- package/dist/snapshots/index.d.cts +2 -2
- package/dist/snapshots/index.d.ts +2 -2
- package/dist/store/index.d.cts +2 -2
- package/dist/store/index.d.ts +2 -2
- package/dist/sync/index.d.cts +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/team/index.d.cts +2 -2
- package/dist/team/index.d.ts +2 -2
- package/dist/{transition-guard-Dmpqzg-_.d.cts → transition-guard-BcLyTGYq.d.cts} +1 -1
- package/dist/{transition-guard-D4bfIAiW.d.ts → transition-guard-Ctxapq1b.d.ts} +1 -1
- package/dist/tx/index.d.cts +2 -2
- package/dist/tx/index.d.ts +2 -2
- package/dist/{types-DyOI6XZ_.d.cts → types-Bhs2i_Ll.d.cts} +260 -4
- package/dist/{types-DLfWFr6U.d.ts → types-DONgts0n.d.ts} +260 -4
- package/dist/{ulid-LaxfH2tK.d.cts → ulid-Bg-IBJyA.d.cts} +1 -1
- package/dist/{ulid-B2L_aqVA.d.ts → ulid-Dwt3JEcy.d.ts} +1 -1
- package/dist/{with-materialized-view-CeZYGJVf.d.cts → with-materialized-view-BYb3p9wT.d.cts} +1 -1
- package/dist/{with-materialized-view-DNULSxoP.d.ts → with-materialized-view-CyVLOr09.d.ts} +1 -1
- package/dist/{with-overlayed-view-C9joG7UZ.d.ts → with-overlayed-view-BhLRxqwI.d.ts} +1 -1
- package/dist/{with-overlayed-view-kdcPGHih.d.cts → with-overlayed-view-LGrQ984e.d.cts} +1 -1
- package/dist/{with-rollup-s58XAeWO.d.cts → with-rollup-Bj8c7ttB.d.cts} +1 -1
- package/dist/{with-rollup-DJDbrxjf.d.ts → with-rollup-CO8ibRcK.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-7PH4OPBZ.js.map +0 -1
- /package/dist/{noydb-ZZCRF6TE.js.map → noydb-VGR2HLDB.js.map} +0 -0
package/dist/bundle/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { T as TransferSealPayload } from '../ulid-
|
|
2
|
-
export { C as COMPRESSION_BROTLI, a as COMPRESSION_GZIP, b as COMPRESSION_NONE, c as CompressionAlgo, F as FLAG_COMPRESSED, d as FLAG_HAS_INTEGRITY_HASH, N as NOYDB_BUNDLE_FORMAT_VERSION, e as NOYDB_BUNDLE_MAGIC, f as NOYDB_BUNDLE_PREFIX_BYTES, g as NoydbBundleHeader, h as NoydbBundleReadResult, R as ReadNoydbBundleOptions, W as WriteNoydbBundleOptions, i as encodeBundleHeader, j as generateULID, k as isULID, r as readNoydbBundle, l as readNoydbBundleHeader, m as resetBrotliSupportCache, v as validateBundleHeader, w as writeNoydbBundle } from '../ulid-
|
|
3
|
-
import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-
|
|
1
|
+
import { T as TransferSealPayload } from '../ulid-Bg-IBJyA.cjs';
|
|
2
|
+
export { C as COMPRESSION_BROTLI, a as COMPRESSION_GZIP, b as COMPRESSION_NONE, c as CompressionAlgo, F as FLAG_COMPRESSED, d as FLAG_HAS_INTEGRITY_HASH, N as NOYDB_BUNDLE_FORMAT_VERSION, e as NOYDB_BUNDLE_MAGIC, f as NOYDB_BUNDLE_PREFIX_BYTES, g as NoydbBundleHeader, h as NoydbBundleReadResult, R as ReadNoydbBundleOptions, W as WriteNoydbBundleOptions, i as encodeBundleHeader, j as generateULID, k as isULID, r as readNoydbBundle, l as readNoydbBundleHeader, m as resetBrotliSupportCache, v as validateBundleHeader, w as writeNoydbBundle } from '../ulid-Bg-IBJyA.cjs';
|
|
3
|
+
import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-Bhs2i_Ll.cjs';
|
|
4
4
|
export { t as AdoptionStateError, B as BackupCorruptedError, u as BackupLedgerError, v as BundleIntegrityError, w as BundleSealMismatchError, x as BundleVersionConflictError, P as PartitionExtractionError, y as TransferSealError } from '../errors-Dkc_fi-S.cjs';
|
|
5
5
|
import '../lazy-builder-eYZzLEL1.cjs';
|
|
6
6
|
import '../predicate-BmhBSPCH.cjs';
|
package/dist/bundle/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { T as TransferSealPayload } from '../ulid-
|
|
2
|
-
export { C as COMPRESSION_BROTLI, a as COMPRESSION_GZIP, b as COMPRESSION_NONE, c as CompressionAlgo, F as FLAG_COMPRESSED, d as FLAG_HAS_INTEGRITY_HASH, N as NOYDB_BUNDLE_FORMAT_VERSION, e as NOYDB_BUNDLE_MAGIC, f as NOYDB_BUNDLE_PREFIX_BYTES, g as NoydbBundleHeader, h as NoydbBundleReadResult, R as ReadNoydbBundleOptions, W as WriteNoydbBundleOptions, i as encodeBundleHeader, j as generateULID, k as isULID, r as readNoydbBundle, l as readNoydbBundleHeader, m as resetBrotliSupportCache, v as validateBundleHeader, w as writeNoydbBundle } from '../ulid-
|
|
3
|
-
import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-
|
|
1
|
+
import { T as TransferSealPayload } from '../ulid-Dwt3JEcy.js';
|
|
2
|
+
export { C as COMPRESSION_BROTLI, a as COMPRESSION_GZIP, b as COMPRESSION_NONE, c as CompressionAlgo, F as FLAG_COMPRESSED, d as FLAG_HAS_INTEGRITY_HASH, N as NOYDB_BUNDLE_FORMAT_VERSION, e as NOYDB_BUNDLE_MAGIC, f as NOYDB_BUNDLE_PREFIX_BYTES, g as NoydbBundleHeader, h as NoydbBundleReadResult, R as ReadNoydbBundleOptions, W as WriteNoydbBundleOptions, i as encodeBundleHeader, j as generateULID, k as isULID, r as readNoydbBundle, l as readNoydbBundleHeader, m as resetBrotliSupportCache, v as validateBundleHeader, w as writeNoydbBundle } from '../ulid-Dwt3JEcy.js';
|
|
3
|
+
import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-DONgts0n.js';
|
|
4
4
|
export { t as AdoptionStateError, B as BackupCorruptedError, u as BackupLedgerError, v as BundleIntegrityError, w as BundleSealMismatchError, x as BundleVersionConflictError, P as PartitionExtractionError, y as TransferSealError } from '../errors-Dkc_fi-S.js';
|
|
5
5
|
import '../lazy-builder-ChSqcF5t.js';
|
|
6
6
|
import '../predicate-BmhBSPCH.js';
|
package/dist/bundle/index.js
CHANGED
|
@@ -543,7 +543,7 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
|
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
545
|
if (isManaged(opts)) {
|
|
546
|
-
const { createNoydb } = await import("../noydb-
|
|
546
|
+
const { createNoydb } = await import("../noydb-VGR2HLDB.js");
|
|
547
547
|
const db = await createNoydb({
|
|
548
548
|
store,
|
|
549
549
|
user: userId,
|
|
@@ -44,6 +44,9 @@ import {
|
|
|
44
44
|
savePersistedSchema,
|
|
45
45
|
saveSealedPassphrase
|
|
46
46
|
} from "./chunk-C6W5KVDV.js";
|
|
47
|
+
import {
|
|
48
|
+
writeNoydbBundle
|
|
49
|
+
} from "./chunk-WE2BUQD2.js";
|
|
47
50
|
import {
|
|
48
51
|
loadPublicEnvelope,
|
|
49
52
|
readPublicEnvelope,
|
|
@@ -4296,6 +4299,265 @@ function valuesMatch(stored, live) {
|
|
|
4296
4299
|
}
|
|
4297
4300
|
}
|
|
4298
4301
|
|
|
4302
|
+
// src/bundle/export-accessible.ts
|
|
4303
|
+
function resolveAccessibleCollections(keyring, scope, writableOnly = false) {
|
|
4304
|
+
let collections;
|
|
4305
|
+
if (keyring.role === "operator" || keyring.role === "client") {
|
|
4306
|
+
collections = Object.entries(keyring.permissions).filter(([, mode]) => !writableOnly || mode === "rw").map(([c]) => c);
|
|
4307
|
+
}
|
|
4308
|
+
if (scope?.collections) {
|
|
4309
|
+
const allow = new Set(scope.collections);
|
|
4310
|
+
collections = (collections ?? [...scope.collections]).filter((c) => allow.has(c));
|
|
4311
|
+
}
|
|
4312
|
+
return collections;
|
|
4313
|
+
}
|
|
4314
|
+
async function buildAccessibleBundle(vault, collections, reKey, compression = "auto") {
|
|
4315
|
+
return writeNoydbBundle(vault, {
|
|
4316
|
+
compression,
|
|
4317
|
+
...collections !== void 0 ? { collections } : {},
|
|
4318
|
+
...reKey ? { exportPassphrase: reKey.passphrase } : {}
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4321
|
+
async function exportAccessibleData(vault, opts = {}) {
|
|
4322
|
+
const { keyring } = vault._introspectState();
|
|
4323
|
+
const collections = resolveAccessibleCollections(keyring, opts.scope);
|
|
4324
|
+
const bytes = await buildAccessibleBundle(vault, collections, opts.reKey, opts.compression ?? "auto");
|
|
4325
|
+
await vault._getLedgerOrNull()?.append({
|
|
4326
|
+
op: "lifecycle",
|
|
4327
|
+
collection: "",
|
|
4328
|
+
id: "",
|
|
4329
|
+
version: 0,
|
|
4330
|
+
actor: keyring.userId,
|
|
4331
|
+
payloadHash: "",
|
|
4332
|
+
reason: `user-export:${keyring.userId}`
|
|
4333
|
+
});
|
|
4334
|
+
return bytes;
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
// src/bundle/withdraw-accessible.ts
|
|
4338
|
+
var FROZEN_SNAPSHOTS_COLLECTION = "_frozen_snapshots";
|
|
4339
|
+
var ENC = new TextEncoder();
|
|
4340
|
+
function randomId() {
|
|
4341
|
+
const b = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
4342
|
+
return Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
4343
|
+
}
|
|
4344
|
+
async function freezeAndDeleteClosure(vault, collections, opts) {
|
|
4345
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
4346
|
+
const closure = [];
|
|
4347
|
+
for (const c of collections) {
|
|
4348
|
+
for (const id of await adapter.list(vaultName, c)) closure.push({ collection: c, id });
|
|
4349
|
+
}
|
|
4350
|
+
let snapshot;
|
|
4351
|
+
if (opts.disposition === "freeze") {
|
|
4352
|
+
const withdrawalId = opts.withdrawalId ?? `wd-${randomId()}`;
|
|
4353
|
+
const snap = {};
|
|
4354
|
+
for (const { collection, id } of closure) {
|
|
4355
|
+
const env = await adapter.get(vaultName, collection, id);
|
|
4356
|
+
if (env) (snap[collection] ??= {})[id] = env;
|
|
4357
|
+
}
|
|
4358
|
+
const frozenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4359
|
+
const body = JSON.stringify({ withdrawalId, frozenAt, by: opts.actorUserId, collections: snap });
|
|
4360
|
+
const sha = await sha256Hex(ENC.encode(body));
|
|
4361
|
+
await adapter.put(
|
|
4362
|
+
vaultName,
|
|
4363
|
+
FROZEN_SNAPSHOTS_COLLECTION,
|
|
4364
|
+
withdrawalId,
|
|
4365
|
+
{ _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: frozenAt, _iv: "", _data: body, _by: opts.actorUserId },
|
|
4366
|
+
0
|
|
4367
|
+
);
|
|
4368
|
+
await vault._getLedgerOrNull()?.append({
|
|
4369
|
+
op: "lifecycle",
|
|
4370
|
+
collection: "",
|
|
4371
|
+
id: "",
|
|
4372
|
+
version: 0,
|
|
4373
|
+
actor: opts.actorUserId,
|
|
4374
|
+
payloadHash: "",
|
|
4375
|
+
reason: `withdrawal-frozen-snapshot:${withdrawalId}:${sha}`
|
|
4376
|
+
});
|
|
4377
|
+
snapshot = { withdrawalId, sha256: sha, recordCount: closure.length, frozenAt };
|
|
4378
|
+
}
|
|
4379
|
+
for (const { collection, id } of closure) {
|
|
4380
|
+
await vault.collection(collection).delete(id);
|
|
4381
|
+
}
|
|
4382
|
+
return snapshot;
|
|
4383
|
+
}
|
|
4384
|
+
async function withdrawAccessibleData(vault, opts) {
|
|
4385
|
+
const { keyring } = vault._introspectState();
|
|
4386
|
+
const disposition = opts.disposition ?? "delete";
|
|
4387
|
+
if (keyring.role === "owner" || keyring.role === "admin") {
|
|
4388
|
+
throw new ReadOnlyError(
|
|
4389
|
+
"unilateralWithdrawal is the scoped self-service path; an owner/admin should use extractPartition"
|
|
4390
|
+
);
|
|
4391
|
+
}
|
|
4392
|
+
if (keyring.role === "client" || keyring.role === "viewer") {
|
|
4393
|
+
throw new ReadOnlyError(
|
|
4394
|
+
"read-only role cannot self-serve a destructive withdrawal \u2014 use requestWithdrawal (two-party)"
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
4397
|
+
const collections = resolveAccessibleCollections(keyring, opts.scope, true);
|
|
4398
|
+
if (!collections || collections.length === 0) {
|
|
4399
|
+
throw new ReadOnlyError(
|
|
4400
|
+
"unilateralWithdrawal requires rw access on the withdrawn collections \u2014 use requestWithdrawal for read-only scope"
|
|
4401
|
+
);
|
|
4402
|
+
}
|
|
4403
|
+
const bundle = await buildAccessibleBundle(vault, collections, opts.reKey);
|
|
4404
|
+
const snapshot = await freezeAndDeleteClosure(vault, collections, {
|
|
4405
|
+
disposition,
|
|
4406
|
+
actorUserId: keyring.userId,
|
|
4407
|
+
...opts.withdrawalId ? { withdrawalId: opts.withdrawalId } : {}
|
|
4408
|
+
});
|
|
4409
|
+
await vault._getLedgerOrNull()?.append({
|
|
4410
|
+
op: "lifecycle",
|
|
4411
|
+
collection: "",
|
|
4412
|
+
id: "",
|
|
4413
|
+
version: 0,
|
|
4414
|
+
actor: keyring.userId,
|
|
4415
|
+
payloadHash: "",
|
|
4416
|
+
reason: `user-unilateral-withdrawal:${keyring.userId}:${disposition}:${opts.legalBasis}`
|
|
4417
|
+
});
|
|
4418
|
+
return snapshot ? { bundle, snapshot } : { bundle };
|
|
4419
|
+
}
|
|
4420
|
+
|
|
4421
|
+
// src/bundle/request-withdrawal.ts
|
|
4422
|
+
var WITHDRAWAL_REQUESTS_COLLECTION = "_user_withdrawal_requests";
|
|
4423
|
+
var WithdrawalRequestError = class extends NoydbError {
|
|
4424
|
+
constructor(message) {
|
|
4425
|
+
super("WITHDRAWAL_REQUEST", message);
|
|
4426
|
+
this.name = "WithdrawalRequestError";
|
|
4427
|
+
}
|
|
4428
|
+
};
|
|
4429
|
+
function writeRequest(vault, req, expectedVersion) {
|
|
4430
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
4431
|
+
const body = JSON.stringify(req);
|
|
4432
|
+
const env = {
|
|
4433
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
4434
|
+
_v: expectedVersion + 1,
|
|
4435
|
+
_ts: req.decidedAt ?? req.requestedAt,
|
|
4436
|
+
_iv: "",
|
|
4437
|
+
_data: body,
|
|
4438
|
+
_by: req.decidedBy ?? req.requester
|
|
4439
|
+
};
|
|
4440
|
+
return adapter.put(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, req.requestId, env, expectedVersion);
|
|
4441
|
+
}
|
|
4442
|
+
async function readRequest(vault, requestId) {
|
|
4443
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
4444
|
+
const env = await adapter.get(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, requestId);
|
|
4445
|
+
if (!env) throw new WithdrawalRequestError(`withdrawal request "${requestId}" not found`);
|
|
4446
|
+
return { req: JSON.parse(env._data), version: env._v };
|
|
4447
|
+
}
|
|
4448
|
+
async function requestWithdrawal(vault, opts = {}) {
|
|
4449
|
+
const { keyring } = vault._introspectState();
|
|
4450
|
+
const collections = resolveAccessibleCollections(keyring, opts.scope, false);
|
|
4451
|
+
if (!collections || collections.length === 0) {
|
|
4452
|
+
throw new WithdrawalRequestError(
|
|
4453
|
+
"requestWithdrawal needs a concrete scope.collections (the caller has all-collection access \u2014 name the collections to withdraw)"
|
|
4454
|
+
);
|
|
4455
|
+
}
|
|
4456
|
+
const requestId = `wr-${randomId()}`;
|
|
4457
|
+
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4458
|
+
const req = {
|
|
4459
|
+
requestId,
|
|
4460
|
+
requester: keyring.userId,
|
|
4461
|
+
collections,
|
|
4462
|
+
disposition: opts.disposition ?? "delete",
|
|
4463
|
+
status: "pending",
|
|
4464
|
+
requestedAt,
|
|
4465
|
+
...opts.legalBasis ? { legalBasis: opts.legalBasis } : {},
|
|
4466
|
+
...opts.expiresInMs ? { expiresAt: new Date(Date.parse(requestedAt) + opts.expiresInMs).toISOString() } : {}
|
|
4467
|
+
};
|
|
4468
|
+
await writeRequest(vault, req, 0);
|
|
4469
|
+
await vault._getLedgerOrNull()?.append({
|
|
4470
|
+
op: "lifecycle",
|
|
4471
|
+
collection: "",
|
|
4472
|
+
id: "",
|
|
4473
|
+
version: 0,
|
|
4474
|
+
actor: keyring.userId,
|
|
4475
|
+
payloadHash: "",
|
|
4476
|
+
reason: `user-withdrawal-request:${requestId}:${keyring.userId}`
|
|
4477
|
+
});
|
|
4478
|
+
return { requestId, status: "pending", ...req.expiresAt ? { expiresAt: req.expiresAt } : {} };
|
|
4479
|
+
}
|
|
4480
|
+
async function listWithdrawalRequests(vault, opts = {}) {
|
|
4481
|
+
const { name: vaultName, adapter } = vault._introspectState();
|
|
4482
|
+
const ids = await adapter.list(vaultName, WITHDRAWAL_REQUESTS_COLLECTION);
|
|
4483
|
+
const out = [];
|
|
4484
|
+
for (const id of ids) {
|
|
4485
|
+
const env = await adapter.get(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, id);
|
|
4486
|
+
if (!env) continue;
|
|
4487
|
+
const req = JSON.parse(env._data);
|
|
4488
|
+
if (!opts.status || req.status === opts.status) out.push(req);
|
|
4489
|
+
}
|
|
4490
|
+
return out;
|
|
4491
|
+
}
|
|
4492
|
+
function assertApprover(vault) {
|
|
4493
|
+
const { keyring } = vault._introspectState();
|
|
4494
|
+
if (keyring.role !== "owner" && keyring.role !== "admin") {
|
|
4495
|
+
throw new WithdrawalRequestError("approveWithdrawal / rejectWithdrawal require an owner or admin");
|
|
4496
|
+
}
|
|
4497
|
+
return keyring.userId;
|
|
4498
|
+
}
|
|
4499
|
+
function assertPending(req) {
|
|
4500
|
+
if (req.status !== "pending") {
|
|
4501
|
+
throw new WithdrawalRequestError(`withdrawal request "${req.requestId}" is already ${req.status}`);
|
|
4502
|
+
}
|
|
4503
|
+
if (req.expiresAt && Date.now() > Date.parse(req.expiresAt)) {
|
|
4504
|
+
throw new WithdrawalRequestError(`withdrawal request "${req.requestId}" expired at ${req.expiresAt}`);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
async function approveWithdrawal(vault, requestId, opts = {}) {
|
|
4508
|
+
const approver = assertApprover(vault);
|
|
4509
|
+
const { req, version } = await readRequest(vault, requestId);
|
|
4510
|
+
assertPending(req);
|
|
4511
|
+
const bundle = await buildAccessibleBundle(vault, [...req.collections], opts.reKey);
|
|
4512
|
+
const snapshot = await freezeAndDeleteClosure(vault, req.collections, {
|
|
4513
|
+
disposition: req.disposition,
|
|
4514
|
+
actorUserId: approver,
|
|
4515
|
+
withdrawalId: `wd-${requestId}`
|
|
4516
|
+
});
|
|
4517
|
+
const decidedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4518
|
+
await writeRequest(vault, {
|
|
4519
|
+
...req,
|
|
4520
|
+
status: "approved",
|
|
4521
|
+
decidedAt,
|
|
4522
|
+
decidedBy: approver,
|
|
4523
|
+
...snapshot ? { snapshotSha256: snapshot.sha256 } : {}
|
|
4524
|
+
}, version);
|
|
4525
|
+
await vault._getLedgerOrNull()?.append({
|
|
4526
|
+
op: "lifecycle",
|
|
4527
|
+
collection: "",
|
|
4528
|
+
id: "",
|
|
4529
|
+
version: 0,
|
|
4530
|
+
actor: approver,
|
|
4531
|
+
payloadHash: "",
|
|
4532
|
+
reason: `user-withdrawal-approved:${requestId}:${req.requester}:${req.disposition}`
|
|
4533
|
+
});
|
|
4534
|
+
return snapshot ? { bundle, snapshot } : { bundle };
|
|
4535
|
+
}
|
|
4536
|
+
async function rejectWithdrawal(vault, requestId, opts = {}) {
|
|
4537
|
+
const approver = assertApprover(vault);
|
|
4538
|
+
const { req, version } = await readRequest(vault, requestId);
|
|
4539
|
+
assertPending(req);
|
|
4540
|
+
const decidedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4541
|
+
const updated = {
|
|
4542
|
+
...req,
|
|
4543
|
+
status: "rejected",
|
|
4544
|
+
decidedAt,
|
|
4545
|
+
decidedBy: approver,
|
|
4546
|
+
...opts.reason ? { rejectReason: opts.reason } : {}
|
|
4547
|
+
};
|
|
4548
|
+
await writeRequest(vault, updated, version);
|
|
4549
|
+
await vault._getLedgerOrNull()?.append({
|
|
4550
|
+
op: "lifecycle",
|
|
4551
|
+
collection: "",
|
|
4552
|
+
id: "",
|
|
4553
|
+
version: 0,
|
|
4554
|
+
actor: approver,
|
|
4555
|
+
payloadHash: "",
|
|
4556
|
+
reason: `user-withdrawal-rejected:${requestId}:${req.requester}`
|
|
4557
|
+
});
|
|
4558
|
+
return updated;
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4299
4561
|
// src/archive/engine.ts
|
|
4300
4562
|
function isHeld(policy, record) {
|
|
4301
4563
|
if (!policy.legalHold) return false;
|
|
@@ -4971,20 +5233,99 @@ var LinkIntegrityError = class extends NoydbError {
|
|
|
4971
5233
|
|
|
4972
5234
|
// src/meta/user-envelope/api.ts
|
|
4973
5235
|
var UserApi = class {
|
|
4974
|
-
constructor(adapter, vaultName, writerKeyringId, getDek, checkGate2) {
|
|
5236
|
+
constructor(adapter, vaultName, writerKeyringId, getDek, checkGate2, exportAccessible, unilateralWithdraw, requestWithdraw, listWithdrawals, approveWithdraw, rejectWithdraw) {
|
|
4975
5237
|
this.adapter = adapter;
|
|
4976
5238
|
this.vaultName = vaultName;
|
|
4977
5239
|
this.writerKeyringId = writerKeyringId;
|
|
4978
5240
|
this.getDek = getDek;
|
|
4979
5241
|
this.checkGate = checkGate2;
|
|
5242
|
+
this.exportAccessible = exportAccessible;
|
|
5243
|
+
this.unilateralWithdraw = unilateralWithdraw;
|
|
5244
|
+
this.requestWithdraw = requestWithdraw;
|
|
5245
|
+
this.listWithdrawals = listWithdrawals;
|
|
5246
|
+
this.approveWithdraw = approveWithdraw;
|
|
5247
|
+
this.rejectWithdraw = rejectWithdraw;
|
|
4980
5248
|
}
|
|
4981
5249
|
adapter;
|
|
4982
5250
|
vaultName;
|
|
4983
5251
|
writerKeyringId;
|
|
4984
5252
|
getDek;
|
|
4985
5253
|
checkGate;
|
|
5254
|
+
exportAccessible;
|
|
5255
|
+
unilateralWithdraw;
|
|
5256
|
+
requestWithdraw;
|
|
5257
|
+
listWithdrawals;
|
|
5258
|
+
approveWithdraw;
|
|
5259
|
+
rejectWithdraw;
|
|
4986
5260
|
/** keyringId → set of listeners. Wildcard '*' fires on every change. */
|
|
4987
5261
|
listeners = /* @__PURE__ */ new Map();
|
|
5262
|
+
/**
|
|
5263
|
+
* #199 P3 — file a two-party withdrawal request for the caller's accessible
|
|
5264
|
+
* scope. Non-destructive (writes a pending request); an owner later approves
|
|
5265
|
+
* or rejects. This is the path for read-only roles (`client`/`viewer`) that
|
|
5266
|
+
* cannot self-serve a destructive `unilateralWithdrawal`. Gated by
|
|
5267
|
+
* `user-request-withdrawal` (enabled by default).
|
|
5268
|
+
*/
|
|
5269
|
+
async requestWithdrawal(opts = {}) {
|
|
5270
|
+
if (this.checkGate) await this.checkGate("user-request-withdrawal");
|
|
5271
|
+
if (!this.requestWithdraw) {
|
|
5272
|
+
throw new Error("requestWithdrawal requires a Noydb-backed vault (not a bare UserApi)");
|
|
5273
|
+
}
|
|
5274
|
+
return this.requestWithdraw(opts);
|
|
5275
|
+
}
|
|
5276
|
+
/** #199 P3 — owner side: list filed withdrawal requests (optionally by status). */
|
|
5277
|
+
async listWithdrawalRequests(opts = {}) {
|
|
5278
|
+
if (!this.listWithdrawals) {
|
|
5279
|
+
throw new Error("listWithdrawalRequests requires a Noydb-backed vault (not a bare UserApi)");
|
|
5280
|
+
}
|
|
5281
|
+
return this.listWithdrawals(opts);
|
|
5282
|
+
}
|
|
5283
|
+
/**
|
|
5284
|
+
* #199 P3 — owner side: approve a pending request. Extracts the requester's
|
|
5285
|
+
* recorded scope under firm authority, disposes of the source per the
|
|
5286
|
+
* request's disposition, and returns the re-keyed bundle to hand back. Gated
|
|
5287
|
+
* by `approve-user-withdrawal` (tier-2 default) + owner/admin role.
|
|
5288
|
+
*/
|
|
5289
|
+
async approveWithdrawal(requestId, opts = {}) {
|
|
5290
|
+
if (this.checkGate) await this.checkGate("approve-user-withdrawal");
|
|
5291
|
+
if (!this.approveWithdraw) {
|
|
5292
|
+
throw new Error("approveWithdrawal requires a Noydb-backed vault (not a bare UserApi)");
|
|
5293
|
+
}
|
|
5294
|
+
return this.approveWithdraw(requestId, opts);
|
|
5295
|
+
}
|
|
5296
|
+
/** #199 P3 — owner side: reject a pending request (no data is touched). */
|
|
5297
|
+
async rejectWithdrawal(requestId, opts = {}) {
|
|
5298
|
+
if (this.checkGate) await this.checkGate("approve-user-withdrawal");
|
|
5299
|
+
if (!this.rejectWithdraw) {
|
|
5300
|
+
throw new Error("rejectWithdrawal requires a Noydb-backed vault (not a bare UserApi)");
|
|
5301
|
+
}
|
|
5302
|
+
return this.rejectWithdraw(requestId, opts);
|
|
5303
|
+
}
|
|
5304
|
+
/**
|
|
5305
|
+
* #199 P2 — single-party withdrawal: export the caller's accessible scope
|
|
5306
|
+
* (re-keyed) and dispose of the source (`delete` or `freeze`). Gated by the
|
|
5307
|
+
* fail-closed built-in `client-unilateral-withdraw` policy — undefined or
|
|
5308
|
+
* disabled → throws (use `requestWithdrawal`). The firm enables it at vault
|
|
5309
|
+
* creation.
|
|
5310
|
+
*/
|
|
5311
|
+
async unilateralWithdrawal(opts) {
|
|
5312
|
+
if (this.checkGate) await this.checkGate("client-unilateral-withdraw");
|
|
5313
|
+
if (!this.unilateralWithdraw) {
|
|
5314
|
+
throw new Error("unilateralWithdrawal requires a Noydb-backed vault (not a bare UserApi)");
|
|
5315
|
+
}
|
|
5316
|
+
return this.unilateralWithdraw(opts);
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* #199 — export the calling user's accessible scope as a portable, re-keyed
|
|
5320
|
+
* `.noydb` bundle. Non-destructive and **always allowed** (data sovereignty
|
|
5321
|
+
* by construction, §11.11) but audited. Scope = the caller's DEK access set.
|
|
5322
|
+
*/
|
|
5323
|
+
async exportMyAccessibleData(opts = {}) {
|
|
5324
|
+
if (!this.exportAccessible) {
|
|
5325
|
+
throw new Error("exportMyAccessibleData requires a Noydb-backed vault (not a bare UserApi)");
|
|
5326
|
+
}
|
|
5327
|
+
return this.exportAccessible(opts);
|
|
5328
|
+
}
|
|
4988
5329
|
// ─── Write-self ──────────────────────────────────────────────────────
|
|
4989
5330
|
/** Read the writer's own envelope. Returns null if never written. */
|
|
4990
5331
|
async me() {
|
|
@@ -6221,7 +6562,13 @@ var Vault = class {
|
|
|
6221
6562
|
this.name,
|
|
6222
6563
|
this.keyring.userId,
|
|
6223
6564
|
() => this.getDEK(USER_ENVELOPE_COLLECTION),
|
|
6224
|
-
(gate, presented) => this.noydb.checkGate(this.name, gate, presented)
|
|
6565
|
+
(gate, presented) => this.noydb.checkGate(this.name, gate, presented),
|
|
6566
|
+
(opts2) => exportAccessibleData(this, opts2),
|
|
6567
|
+
(opts2) => withdrawAccessibleData(this, opts2),
|
|
6568
|
+
(opts2) => requestWithdrawal(this, opts2),
|
|
6569
|
+
(opts2) => listWithdrawalRequests(this, opts2),
|
|
6570
|
+
(requestId, opts2) => approveWithdrawal(this, requestId, opts2),
|
|
6571
|
+
(requestId, opts2) => rejectWithdrawal(this, requestId, opts2)
|
|
6225
6572
|
);
|
|
6226
6573
|
}
|
|
6227
6574
|
/**
|
|
@@ -8612,6 +8959,7 @@ var Vault = class {
|
|
|
8612
8959
|
collectionCache: this.collectionCache,
|
|
8613
8960
|
refRegistry: this.refRegistry,
|
|
8614
8961
|
getDEK: this.getDEK,
|
|
8962
|
+
keyring: this.keyring,
|
|
8615
8963
|
subsystems: {
|
|
8616
8964
|
guards: this.guardRegistry !== null,
|
|
8617
8965
|
derivations: this.derivationRegistry !== null,
|
|
@@ -10005,7 +10353,18 @@ var PERSONAL_POLICY = Object.freeze({
|
|
|
10005
10353
|
// Setting `enabled: false` makes vault.user.list() return only
|
|
10006
10354
|
// self (privacy-strict opt-out).
|
|
10007
10355
|
"edit-own-profile": { minTier: 3 },
|
|
10008
|
-
"view-team-profiles": { minTier: 2 }
|
|
10356
|
+
"view-team-profiles": { minTier: 2 },
|
|
10357
|
+
// client-unilateral-withdraw: a non-owner's self-service DESTRUCTIVE
|
|
10358
|
+
// withdrawal (export-and-delete/freeze, #199). Fail-closed by default —
|
|
10359
|
+
// the firm opts in per jurisdiction/contract (e.g. GDPR Art. 17).
|
|
10360
|
+
// Listed explicitly (not just relying on the built-in default) so it is
|
|
10361
|
+
// discoverable in describeGate / policy dumps.
|
|
10362
|
+
"client-unilateral-withdraw": { minTier: 1, enabled: false },
|
|
10363
|
+
// Two-party withdrawal (#199 P3): filing a request is non-destructive
|
|
10364
|
+
// (tier-1, enabled so a read-only client can ask); deciding it is the
|
|
10365
|
+
// destructive step (tier-2 floor + owner/admin role, enforced in code).
|
|
10366
|
+
"user-request-withdrawal": { minTier: 1 },
|
|
10367
|
+
"approve-user-withdrawal": { minTier: 2 }
|
|
10009
10368
|
}
|
|
10010
10369
|
});
|
|
10011
10370
|
var STRICT_POLICY = Object.freeze({
|
|
@@ -10104,7 +10463,24 @@ var STRICT_POLICY = Object.freeze({
|
|
|
10104
10463
|
minTier: 2,
|
|
10105
10464
|
factors: [{ anyOf: ["totp", "email-otp"] }]
|
|
10106
10465
|
},
|
|
10107
|
-
"view-team-profiles": { minTier: 2 }
|
|
10466
|
+
"view-team-profiles": { minTier: 2 },
|
|
10467
|
+
// STRICT: still fail-closed, but if a regulated firm flips enabled:true
|
|
10468
|
+
// they inherit a two-factor proof + shared-device block for the
|
|
10469
|
+
// destructive withdrawal (mirrors export-plaintext's hardening).
|
|
10470
|
+
"client-unilateral-withdraw": {
|
|
10471
|
+
minTier: 1,
|
|
10472
|
+
enabled: false,
|
|
10473
|
+
factors: [{ anyOf: ["totp", "email-otp"], count: 2 }],
|
|
10474
|
+
warn: { sharedDevice: "block" }
|
|
10475
|
+
},
|
|
10476
|
+
// STRICT: filing stays tier-1; the destructive APPROVE demands an
|
|
10477
|
+
// off-device factor + shared-device block (mirrors export-bundle).
|
|
10478
|
+
"user-request-withdrawal": { minTier: 1 },
|
|
10479
|
+
"approve-user-withdrawal": {
|
|
10480
|
+
minTier: 2,
|
|
10481
|
+
factors: [{ anyOf: ["totp", "email-otp"] }],
|
|
10482
|
+
warn: { sharedDevice: "block" }
|
|
10483
|
+
}
|
|
10108
10484
|
}
|
|
10109
10485
|
});
|
|
10110
10486
|
function mergePolicy(base, override) {
|
|
@@ -12526,6 +12902,13 @@ export {
|
|
|
12526
12902
|
compileSequenceFormat,
|
|
12527
12903
|
resolveSequenceKey,
|
|
12528
12904
|
SequenceStore,
|
|
12905
|
+
exportAccessibleData,
|
|
12906
|
+
withdrawAccessibleData,
|
|
12907
|
+
WithdrawalRequestError,
|
|
12908
|
+
requestWithdrawal,
|
|
12909
|
+
listWithdrawalRequests,
|
|
12910
|
+
approveWithdrawal,
|
|
12911
|
+
rejectWithdrawal,
|
|
12529
12912
|
validateSchemaInput,
|
|
12530
12913
|
validateSchemaOutput,
|
|
12531
12914
|
isZodSchema,
|
|
@@ -12569,4 +12952,4 @@ export {
|
|
|
12569
12952
|
Noydb,
|
|
12570
12953
|
createNoydb
|
|
12571
12954
|
};
|
|
12572
|
-
//# sourceMappingURL=chunk-
|
|
12955
|
+
//# sourceMappingURL=chunk-P65YMN5V.js.map
|