@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.
Files changed (69) hide show
  1. package/dist/attestation/index.d.cts +1 -1
  2. package/dist/attestation/index.d.ts +1 -1
  3. package/dist/blobs/index.d.cts +3 -3
  4. package/dist/blobs/index.d.ts +3 -3
  5. package/dist/bundle/index.cjs +21664 -21247
  6. package/dist/bundle/index.cjs.map +1 -1
  7. package/dist/bundle/index.d.cts +3 -3
  8. package/dist/bundle/index.d.ts +3 -3
  9. package/dist/bundle/index.js +1 -1
  10. package/dist/{chunk-7PH4OPBZ.js → chunk-P65YMN5V.js} +388 -5
  11. package/dist/chunk-P65YMN5V.js.map +1 -0
  12. package/dist/consent/index.d.cts +2 -2
  13. package/dist/consent/index.d.ts +2 -2
  14. package/dist/derivations/index.d.cts +3 -3
  15. package/dist/derivations/index.d.ts +3 -3
  16. package/dist/{dev-unlock-CY0HIZA0.d.cts → dev-unlock-Bw7iBD1D.d.cts} +1 -1
  17. package/dist/{dev-unlock-CpKSkl2c.d.ts → dev-unlock-DzDzLTdZ.d.ts} +1 -1
  18. package/dist/guards/index.d.cts +3 -3
  19. package/dist/guards/index.d.ts +3 -3
  20. package/dist/{hash-BSd0-_L8.d.cts → hash-C52X_-m5.d.cts} +1 -1
  21. package/dist/{hash-BnBQx39y.d.ts → hash-DepR-xVc.d.ts} +1 -1
  22. package/dist/history/index.d.cts +3 -3
  23. package/dist/history/index.d.ts +3 -3
  24. package/dist/i18n/index.d.cts +2 -2
  25. package/dist/i18n/index.d.ts +2 -2
  26. package/dist/index.cjs +396 -4
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +10 -10
  29. package/dist/index.d.ts +10 -10
  30. package/dist/index.js +16 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/materialized-views/index.d.cts +3 -3
  33. package/dist/materialized-views/index.d.ts +3 -3
  34. package/dist/{mime-magic-BnJCGJzB.d.cts → mime-magic-Cxf9B_Dm.d.cts} +1 -1
  35. package/dist/{mime-magic-CjSyakO4.d.ts → mime-magic-Dejetix_.d.ts} +1 -1
  36. package/dist/{noydb-ZZCRF6TE.js → noydb-VGR2HLDB.js} +3 -2
  37. package/dist/overlay-views/index.d.cts +3 -3
  38. package/dist/overlay-views/index.d.ts +3 -3
  39. package/dist/periods/index.d.cts +2 -2
  40. package/dist/periods/index.d.ts +2 -2
  41. package/dist/session/index.d.cts +3 -3
  42. package/dist/session/index.d.ts +3 -3
  43. package/dist/shadow/index.d.cts +2 -2
  44. package/dist/shadow/index.d.ts +2 -2
  45. package/dist/snapshots/index.d.cts +2 -2
  46. package/dist/snapshots/index.d.ts +2 -2
  47. package/dist/store/index.d.cts +2 -2
  48. package/dist/store/index.d.ts +2 -2
  49. package/dist/sync/index.d.cts +1 -1
  50. package/dist/sync/index.d.ts +1 -1
  51. package/dist/team/index.d.cts +2 -2
  52. package/dist/team/index.d.ts +2 -2
  53. package/dist/{transition-guard-Dmpqzg-_.d.cts → transition-guard-BcLyTGYq.d.cts} +1 -1
  54. package/dist/{transition-guard-D4bfIAiW.d.ts → transition-guard-Ctxapq1b.d.ts} +1 -1
  55. package/dist/tx/index.d.cts +2 -2
  56. package/dist/tx/index.d.ts +2 -2
  57. package/dist/{types-DyOI6XZ_.d.cts → types-Bhs2i_Ll.d.cts} +260 -4
  58. package/dist/{types-DLfWFr6U.d.ts → types-DONgts0n.d.ts} +260 -4
  59. package/dist/{ulid-LaxfH2tK.d.cts → ulid-Bg-IBJyA.d.cts} +1 -1
  60. package/dist/{ulid-B2L_aqVA.d.ts → ulid-Dwt3JEcy.d.ts} +1 -1
  61. package/dist/{with-materialized-view-CeZYGJVf.d.cts → with-materialized-view-BYb3p9wT.d.cts} +1 -1
  62. package/dist/{with-materialized-view-DNULSxoP.d.ts → with-materialized-view-CyVLOr09.d.ts} +1 -1
  63. package/dist/{with-overlayed-view-C9joG7UZ.d.ts → with-overlayed-view-BhLRxqwI.d.ts} +1 -1
  64. package/dist/{with-overlayed-view-kdcPGHih.d.cts → with-overlayed-view-LGrQ984e.d.cts} +1 -1
  65. package/dist/{with-rollup-s58XAeWO.d.cts → with-rollup-Bj8c7ttB.d.cts} +1 -1
  66. package/dist/{with-rollup-DJDbrxjf.d.ts → with-rollup-CO8ibRcK.d.ts} +1 -1
  67. package/package.json +3 -3
  68. package/dist/chunk-7PH4OPBZ.js.map +0 -1
  69. /package/dist/{noydb-ZZCRF6TE.js.map → noydb-VGR2HLDB.js.map} +0 -0
@@ -1,6 +1,6 @@
1
- import { T as TransferSealPayload } from '../ulid-LaxfH2tK.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-LaxfH2tK.cjs';
3
- import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-DyOI6XZ_.cjs';
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';
@@ -1,6 +1,6 @@
1
- import { T as TransferSealPayload } from '../ulid-B2L_aqVA.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-B2L_aqVA.js';
3
- import { bh as Vault, aW as NoydbStore, bd as SealingKeyProvider, bi as RecoveryEnrollmentInput, bj as ShamirRecoveryProvider } from '../types-DLfWFr6U.js';
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';
@@ -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-ZZCRF6TE.js");
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-7PH4OPBZ.js.map
12955
+ //# sourceMappingURL=chunk-P65YMN5V.js.map