@powerhousedao/powerhouse-vetra-packages 6.2.2-dev.4 → 6.2.2-dev.41

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 (100) hide show
  1. package/dist/browser/assets/{entry-Cvoo4_c9.js → entry-Db_lIhQq.js} +3 -2
  2. package/dist/browser/assets/{projection-entry-DVec8YRh.js → projection-entry-DqgHaUSy.js} +4 -4
  3. package/dist/browser/{connect-CV-z3DqA.js → connect-BUnNjGUW.js} +874 -200
  4. package/dist/browser/connect-BUnNjGUW.js.map +1 -0
  5. package/dist/browser/{dist-CXoKspdx.js → dist-B4FWbuEM.js} +10 -2
  6. package/dist/browser/{dist-CXoKspdx.js.map → dist-B4FWbuEM.js.map} +1 -1
  7. package/dist/{node/dist-BeZaEP6S.mjs → browser/dist-BAJ5nRDl.js} +21 -2
  8. package/dist/browser/{dist-D8H48e8N.js.map → dist-BAJ5nRDl.js.map} +1 -1
  9. package/dist/browser/{dist-BEs8jMgM.js → dist-BatmWBzA.js} +1782 -1640
  10. package/dist/browser/dist-BatmWBzA.js.map +1 -0
  11. package/dist/browser/{document-drive-sTKoi6L4.js → document-drive-D3Z9vXTQ.js} +864 -118
  12. package/dist/browser/document-drive-D3Z9vXTQ.js.map +1 -0
  13. package/dist/browser/document-models/index.js +1 -1
  14. package/dist/browser/{documents-D4ek7Ubv.js → documents-J_l59mSV.js} +5 -604
  15. package/dist/browser/documents-J_l59mSV.js.map +1 -0
  16. package/dist/browser/editor-BLeFoOh6.js +25 -0
  17. package/dist/browser/editor-BLeFoOh6.js.map +1 -0
  18. package/dist/browser/{editor-Cw38euRs.js → editor-Bn6uXvxG.js} +10 -142
  19. package/dist/browser/editor-Bn6uXvxG.js.map +1 -0
  20. package/dist/browser/editors/document-model-editor/module.js +1 -1
  21. package/dist/browser/editors/generic-drive-explorer/index.js +5 -5
  22. package/dist/browser/editors/generic-drive-explorer/module.js +1 -1
  23. package/dist/{node/folder-view-DjKO1mAh.mjs → browser/folder-view-DNzFnfW2.js} +53 -45
  24. package/dist/browser/folder-view-DNzFnfW2.js.map +1 -0
  25. package/dist/browser/{graphql-editor-B-dNI5hD.js → graphql-editor-DWo8N_z4.js} +8 -7
  26. package/dist/browser/{graphql-editor-B-dNI5hD.js.map → graphql-editor-DWo8N_z4.js.map} +1 -1
  27. package/dist/browser/{hooks-CORHBNHb.js → hooks-KKcNp2X0.js} +2 -2
  28. package/dist/browser/{hooks-CORHBNHb.js.map → hooks-KKcNp2X0.js.map} +1 -1
  29. package/dist/browser/index.js +2 -2
  30. package/dist/browser/index.js.map +1 -1
  31. package/dist/browser/{json-editor-v_MnzQVs.js → json-editor-CEugjpFj.js} +4 -4
  32. package/dist/browser/{json-editor-v_MnzQVs.js.map → json-editor-CEugjpFj.js.map} +1 -1
  33. package/dist/browser/{lib-C-f3izXz.js → lib-CiaHsAaR.js} +12 -2
  34. package/dist/browser/lib-CiaHsAaR.js.map +1 -0
  35. package/dist/browser/{linting-Dn6pXRkK.js → linting-_AQWr8aE.js} +5 -4
  36. package/dist/browser/{linting-Dn6pXRkK.js.map → linting-_AQWr8aE.js.map} +1 -1
  37. package/dist/browser/printer-CEYr4JOg.js +604 -0
  38. package/dist/browser/printer-CEYr4JOg.js.map +1 -0
  39. package/dist/browser/{schema-context-DY-GEGnY.js → schema-context-ZyeclQwy.js} +2 -2
  40. package/dist/browser/{schema-context-DY-GEGnY.js.map → schema-context-ZyeclQwy.js.map} +1 -1
  41. package/dist/browser/{state-schemas-65Tp0ZuG.js → state-schemas-VrticHe4.js} +7 -7
  42. package/dist/browser/{state-schemas-65Tp0ZuG.js.map → state-schemas-VrticHe4.js.map} +1 -1
  43. package/dist/node/{connect-Bbi5wHyR.mjs → connect-wMkkAckX.mjs} +874 -200
  44. package/dist/node/connect-wMkkAckX.mjs.map +1 -0
  45. package/dist/node/{dist-8nCvrie2.mjs → dist-CUHBOv3P.mjs} +1593 -1446
  46. package/dist/node/dist-CUHBOv3P.mjs.map +1 -0
  47. package/dist/{browser/dist-D8H48e8N.js → node/dist-CzYOpeXN.mjs} +21 -2
  48. package/dist/node/{dist-BeZaEP6S.mjs.map → dist-CzYOpeXN.mjs.map} +1 -1
  49. package/dist/node/{dist-fA4EItr0.mjs → dist-DT7jI9ol.mjs} +10 -2
  50. package/dist/node/{dist-fA4EItr0.mjs.map → dist-DT7jI9ol.mjs.map} +1 -1
  51. package/dist/node/{document-drive-DjL9iikf.mjs → document-drive-CI0J7nMj.mjs} +864 -118
  52. package/dist/node/document-drive-CI0J7nMj.mjs.map +1 -0
  53. package/dist/node/document-models/index.mjs +1 -1
  54. package/dist/node/{documents-BFBPmyw1.mjs → documents-DCf53bzO.mjs} +3 -3
  55. package/dist/node/{documents-BFBPmyw1.mjs.map → documents-DCf53bzO.mjs.map} +1 -1
  56. package/dist/node/{editor-DkT0krRE.mjs → editor-Cr-mNQca.mjs} +10 -142
  57. package/dist/node/editor-Cr-mNQca.mjs.map +1 -0
  58. package/dist/node/editor-aIeTo2EE.mjs +25 -0
  59. package/dist/node/editor-aIeTo2EE.mjs.map +1 -0
  60. package/dist/node/editors/document-model-editor/module.mjs +1 -1
  61. package/dist/node/editors/generic-drive-explorer/index.mjs +5 -5
  62. package/dist/node/editors/generic-drive-explorer/module.mjs +1 -1
  63. package/dist/{browser/folder-view-BQWrt8wh.js → node/folder-view-_x1OBYZN.mjs} +53 -45
  64. package/dist/node/folder-view-_x1OBYZN.mjs.map +1 -0
  65. package/dist/node/{graphql-DskHmkMe.mjs → graphql-BfAoPeWM.mjs} +2 -2
  66. package/dist/node/{graphql-DskHmkMe.mjs.map → graphql-BfAoPeWM.mjs.map} +1 -1
  67. package/dist/node/{graphql-editor-x-d_DoqN.mjs → graphql-editor-Dd2_z5QJ.mjs} +7 -7
  68. package/dist/node/{graphql-editor-x-d_DoqN.mjs.map → graphql-editor-Dd2_z5QJ.mjs.map} +1 -1
  69. package/dist/node/{hooks-DJrjfVaS.mjs → hooks-D0DcS3MV.mjs} +2 -2
  70. package/dist/node/{hooks-DJrjfVaS.mjs.map → hooks-D0DcS3MV.mjs.map} +1 -1
  71. package/dist/node/index.mjs +2 -2
  72. package/dist/node/index.mjs.map +1 -1
  73. package/dist/node/{json-editor-D8QbP3jl.mjs → json-editor-Dphg6ZeL.mjs} +4 -4
  74. package/dist/node/{json-editor-D8QbP3jl.mjs.map → json-editor-Dphg6ZeL.mjs.map} +1 -1
  75. package/dist/node/{linting-Dx50GuLN.mjs → linting-BjvRPjCY.mjs} +5 -5
  76. package/dist/node/{linting-Dx50GuLN.mjs.map → linting-BjvRPjCY.mjs.map} +1 -1
  77. package/dist/node/{main-D82dkD7g.mjs → main-Db0ei9wX.mjs} +14 -4
  78. package/dist/node/main-Db0ei9wX.mjs.map +1 -0
  79. package/dist/node/{schema-context-CX5ml_9t.mjs → schema-context-DL8jhi_d.mjs} +3 -3
  80. package/dist/node/{schema-context-CX5ml_9t.mjs.map → schema-context-DL8jhi_d.mjs.map} +1 -1
  81. package/dist/node/{state-schemas-BwvZVEhR.mjs → state-schemas-BSLgTw2O.mjs} +8 -8
  82. package/dist/node/{state-schemas-BwvZVEhR.mjs.map → state-schemas-BSLgTw2O.mjs.map} +1 -1
  83. package/package.json +6 -6
  84. package/dist/browser/connect-CV-z3DqA.js.map +0 -1
  85. package/dist/browser/dist-BEs8jMgM.js.map +0 -1
  86. package/dist/browser/document-drive-sTKoi6L4.js.map +0 -1
  87. package/dist/browser/documents-D4ek7Ubv.js.map +0 -1
  88. package/dist/browser/editor-Cw38euRs.js.map +0 -1
  89. package/dist/browser/editor-DY3JsQJ3.js +0 -29
  90. package/dist/browser/editor-DY3JsQJ3.js.map +0 -1
  91. package/dist/browser/folder-view-BQWrt8wh.js.map +0 -1
  92. package/dist/browser/lib-C-f3izXz.js.map +0 -1
  93. package/dist/node/connect-Bbi5wHyR.mjs.map +0 -1
  94. package/dist/node/dist-8nCvrie2.mjs.map +0 -1
  95. package/dist/node/document-drive-DjL9iikf.mjs.map +0 -1
  96. package/dist/node/editor-BH2gCDjt.mjs +0 -29
  97. package/dist/node/editor-BH2gCDjt.mjs.map +0 -1
  98. package/dist/node/editor-DkT0krRE.mjs.map +0 -1
  99. package/dist/node/folder-view-DjKO1mAh.mjs.map +0 -1
  100. package/dist/node/main-D82dkD7g.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { E as __commonJSMin, _ as string, c as any, f as lazy, h as object, k as __toESM, l as array, m as number, n as constantCase, o as _enum, p as literal, r as pascalCase, u as boolean, v as union, x as ZodError, y as unknown } from "./dist-CXoKspdx.js";
1
+ import { A as __toESM, D as __commonJSMin, S as ZodError, _ as strictObject, b as unknown, c as any, f as lazy, g as record, h as object, l as array, m as number, n as constantCase, o as _enum, p as literal, r as pascalCase, u as boolean, v as string, y as union } from "./dist-B4FWbuEM.js";
2
2
  //#endregion
3
3
  //#region ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
4
4
  var import_safe_stable_stringify = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -589,7 +589,7 @@ function isBE() {
589
589
  return new Uint8Array(new Uint16Array([65279]).buffer)[0] === 254;
590
590
  }
591
591
  //#endregion
592
- //#region ../shared/dist/utils-DKOFCM0Q.js
592
+ //#region ../shared/dist/utils-CTt1XRn-.js
593
593
  const hashBrowser = (data, algorithm = "sha1", encoding = "base64", _params) => {
594
594
  if (!["sha1"].includes(algorithm)) throw new Error(`Hashing algorithm not supported: "${algorithm}". Available: sha1`);
595
595
  if (!["base64", "hex"].includes(encoding)) throw new Error(`Hash encoding not supported: "${encoding}". Available: base64, hex`);
@@ -636,6 +636,40 @@ function ab2hex(ab) {
636
636
  const view = ArrayBuffer.isView(ab) ? ab : new Uint8Array(ab);
637
637
  return Array.prototype.map.call(view, (x) => ("00" + x.toString(16)).slice(-2)).join("");
638
638
  }
639
+ const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
640
+ /**
641
+ * Decodes a base58btc (Bitcoin/IPFS alphabet) string to bytes. Returns null on
642
+ * any character outside the alphabet.
643
+ */
644
+ function base58Decode(input) {
645
+ const bytes = [];
646
+ for (const ch of input) {
647
+ let carry = BASE58_ALPHABET.indexOf(ch);
648
+ if (carry === -1) return null;
649
+ for (let i = 0; i < bytes.length; i++) {
650
+ carry += bytes[i] * 58;
651
+ bytes[i] = carry & 255;
652
+ carry >>= 8;
653
+ }
654
+ while (carry > 0) {
655
+ bytes.push(carry & 255);
656
+ carry >>= 8;
657
+ }
658
+ }
659
+ for (let k = 0; k < input.length && input[k] === "1"; k++) bytes.push(0);
660
+ return Uint8Array.from(bytes.reverse());
661
+ }
662
+ /** Decodes a base64url string to bytes (portable across node, browsers, workers). */
663
+ function base64UrlToBytes(input) {
664
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
665
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
666
+ let binary;
667
+ if (typeof atob === "function") binary = atob(padded);
668
+ else binary = Buffer.from(padded, "base64").toString("binary");
669
+ const bytes = new Uint8Array(binary.length);
670
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
671
+ return bytes;
672
+ }
639
673
  function generateId(method) {
640
674
  if (method && method.toString() !== "UUIDv4") throw new Error(`Id generation method not supported: "${method.toString()}"`);
641
675
  return globalThis.crypto.randomUUID();
@@ -2978,6 +3012,75 @@ var DowngradeNotSupportedError = class extends Error {
2978
3012
  this.toVersion = toVersion;
2979
3013
  }
2980
3014
  };
3015
+ /**
3016
+ * Thrown when INITIALIZE_AUTH is applied to an already-initialized auth scope.
3017
+ * The genesis action is valid only at auth revision zero.
3018
+ */
3019
+ var AuthAlreadyInitializedError = class extends Error {
3020
+ documentId;
3021
+ constructor(documentId) {
3022
+ super(`Auth scope already initialized for document ${documentId}: INITIALIZE_AUTH is valid only at auth revision zero`);
3023
+ this.name = "AuthAlreadyInitializedError";
3024
+ this.documentId = documentId;
3025
+ }
3026
+ };
3027
+ /**
3028
+ * Thrown when INITIALIZE_AUTH is not signed by the document creator (its signer
3029
+ * does not match `header.sig.publicKey`), so it cannot set the auth policy.
3030
+ */
3031
+ var AuthInitializerNotCreatorError = class extends Error {
3032
+ documentId;
3033
+ constructor(documentId) {
3034
+ super(`INITIALIZE_AUTH for document ${documentId} must be signed by the document creator`);
3035
+ this.name = "AuthInitializerNotCreatorError";
3036
+ this.documentId = documentId;
3037
+ }
3038
+ };
3039
+ /**
3040
+ * Thrown when INITIALIZE_AUTH carries a version below 1. Version 0 is reserved
3041
+ * for the uninitialized auth scope.
3042
+ */
3043
+ var InvalidAuthVersionError = class extends Error {
3044
+ documentId;
3045
+ version;
3046
+ constructor(documentId, version) {
3047
+ super(`Invalid auth policy version ${version} for document ${documentId}: INITIALIZE_AUTH requires an integer version >= 1`);
3048
+ this.name = "InvalidAuthVersionError";
3049
+ this.documentId = documentId;
3050
+ this.version = version;
3051
+ }
3052
+ };
3053
+ /** Thrown when a duplicate would lose the source policy's version or creator binding. */
3054
+ var AuthPolicyNotPreservedError = class extends Error {
3055
+ documentId;
3056
+ constructor(documentId) {
3057
+ super(`Duplicating document ${documentId} would not preserve its auth policy: the copy loses the policy version or its creator binding`);
3058
+ this.name = "AuthPolicyNotPreservedError";
3059
+ this.documentId = documentId;
3060
+ }
3061
+ };
3062
+ /**
3063
+ * Thrown when a grant referenced by REMOVE_GRANT or MOVE_GRANT does not exist.
3064
+ */
3065
+ var GrantNotFoundError = class extends Error {
3066
+ grantId;
3067
+ constructor(grantId) {
3068
+ super(`Grant not found in auth scope: ${grantId}`);
3069
+ this.name = "GrantNotFoundError";
3070
+ this.grantId = grantId;
3071
+ }
3072
+ };
3073
+ /**
3074
+ * Thrown when a disallowed action (UNDO, REDO, PRUNE) targets the auth scope.
3075
+ */
3076
+ var AuthActionNotAllowedError = class extends Error {
3077
+ actionType;
3078
+ constructor(actionType) {
3079
+ super(`${actionType} is not permitted on the auth scope`);
3080
+ this.name = "AuthActionNotAllowedError";
3081
+ this.actionType = actionType;
3082
+ }
3083
+ };
2981
3084
  var HashMismatchError = class extends Error {
2982
3085
  _scope;
2983
3086
  _document;
@@ -3444,17 +3547,101 @@ const ConfigEntrySchema = object({
3444
3547
  required: boolean().optional(),
3445
3548
  default: boolean().optional()
3446
3549
  });
3550
+ const PwaUrlPatternSchema = union([string(), strictObject({
3551
+ source: string(),
3552
+ flags: string().optional()
3553
+ }).refine((p) => {
3554
+ try {
3555
+ new RegExp(p.source, p.flags);
3556
+ return true;
3557
+ } catch {
3558
+ return false;
3559
+ }
3560
+ }, { message: "source/flags do not compile to a valid RegExp" })]);
3561
+ const PwaRuntimeCachingSchema = strictObject({
3562
+ urlPattern: PwaUrlPatternSchema,
3563
+ handler: _enum([
3564
+ "CacheFirst",
3565
+ "CacheOnly",
3566
+ "NetworkFirst",
3567
+ "NetworkOnly",
3568
+ "StaleWhileRevalidate"
3569
+ ]),
3570
+ method: _enum([
3571
+ "GET",
3572
+ "POST",
3573
+ "PUT",
3574
+ "DELETE",
3575
+ "HEAD",
3576
+ "PATCH"
3577
+ ]).optional(),
3578
+ options: strictObject({
3579
+ cacheName: string().optional(),
3580
+ networkTimeoutSeconds: number().optional(),
3581
+ expiration: strictObject({
3582
+ maxEntries: number().optional(),
3583
+ maxAgeSeconds: number().optional()
3584
+ }).optional(),
3585
+ cacheableResponse: strictObject({
3586
+ statuses: array(number()).optional(),
3587
+ headers: record(string(), string()).optional()
3588
+ }).optional()
3589
+ }).optional()
3590
+ });
3591
+ const PwaIconSchema = strictObject({
3592
+ src: string(),
3593
+ sizes: string().optional(),
3594
+ type: string().optional(),
3595
+ purpose: string().optional()
3596
+ });
3597
+ const PwaFileHandlerSchema = strictObject({
3598
+ accept: record(string(), array(string().regex(/^\./, "file extensions must start with '.'"))),
3599
+ icons: array(PwaIconSchema).optional(),
3600
+ launch_type: _enum(["single-client", "multiple-clients"]).optional()
3601
+ });
3602
+ const PwaConfigSchema = strictObject({
3603
+ manifest: strictObject({
3604
+ name: string().optional(),
3605
+ short_name: string().optional(),
3606
+ description: string().optional(),
3607
+ theme_color: string().optional(),
3608
+ background_color: string().optional(),
3609
+ display: _enum([
3610
+ "fullscreen",
3611
+ "standalone",
3612
+ "minimal-ui",
3613
+ "browser"
3614
+ ]).optional(),
3615
+ start_url: string().optional(),
3616
+ scope: string().optional(),
3617
+ icons: array(PwaIconSchema).optional(),
3618
+ file_handlers: array(PwaFileHandlerSchema).optional(),
3619
+ launch_handler: strictObject({ client_mode: _enum([
3620
+ "auto",
3621
+ "focus-existing",
3622
+ "navigate-existing",
3623
+ "navigate-new"
3624
+ ]) }).optional()
3625
+ }).optional(),
3626
+ globPatterns: array(string()).optional(),
3627
+ globIgnores: array(string()).optional(),
3628
+ maximumFileSizeToCacheInBytes: number().optional(),
3629
+ runtimeCaching: array(PwaRuntimeCachingSchema).optional(),
3630
+ navigateFallbackDenylist: array(PwaUrlPatternSchema).optional()
3631
+ });
3447
3632
  object({
3448
3633
  name: string(),
3449
3634
  description: string().optional(),
3450
3635
  category: string().optional(),
3636
+ image: string().optional(),
3451
3637
  publisher: PublisherSchema.optional(),
3452
3638
  documentModels: PowerhouseModulesSchema,
3453
3639
  apps: PowerhouseModulesSchema,
3454
3640
  editors: PowerhouseModulesSchema,
3455
3641
  processors: PowerhouseModulesSchema,
3456
3642
  subgraphs: PowerhouseModulesSchema,
3457
- config: array(ConfigEntrySchema).optional()
3643
+ config: array(ConfigEntrySchema).optional(),
3644
+ pwa: PwaConfigSchema.optional()
3458
3645
  });
3459
3646
  /**
3460
3647
  * Cancels the last `count` operations.
@@ -3725,6 +3912,312 @@ const actions = {
3725
3912
  ...baseActions,
3726
3913
  ...documentModelActions
3727
3914
  };
3915
+ const documentModelDocumentType = "powerhouse/document-model";
3916
+ /**
3917
+ * Thrown when a grant violates the v1 validation rules. The message is stored
3918
+ * on error operations, so it must be a pure function of the input.
3919
+ */
3920
+ var InvalidGrantError = class extends Error {
3921
+ grantId;
3922
+ constructor(grantId, problem) {
3923
+ super(`Invalid grant "${grantId}": ${problem}`);
3924
+ this.name = "InvalidGrantError";
3925
+ this.grantId = grantId;
3926
+ }
3927
+ };
3928
+ /** Thrown for a `{ group }` principal on a group document: references never chain. */
3929
+ var GroupPrincipalNotAllowedError = class extends Error {
3930
+ grantId;
3931
+ constructor(grantId) {
3932
+ super(`Grant "${grantId}" uses a group principal on a group document: a group's auth scope cannot reference other groups`);
3933
+ this.name = "GroupPrincipalNotAllowedError";
3934
+ this.grantId = grantId;
3935
+ }
3936
+ };
3937
+ /**
3938
+ * Thrown when a change would leave a creator-less policy with no grant
3939
+ * permitting execute on the auth scope. Without the creator carve-out no
3940
+ * subject could ever administer such a policy again.
3941
+ */
3942
+ var AuthAdministrationLockoutError = class extends Error {
3943
+ grantId;
3944
+ constructor(grantId) {
3945
+ super(`Change to grant "${grantId}" would leave no reachable grant permitting execute on the auth scope: a policy with no creator must always retain one`);
3946
+ this.name = "AuthAdministrationLockoutError";
3947
+ this.grantId = grantId;
3948
+ }
3949
+ };
3950
+ /**
3951
+ * Thrown when INITIALIZE_AUTH would create a creator-less policy with no grant
3952
+ * permitting execute on the auth scope. Such a policy would be born with no
3953
+ * subject able to administer it.
3954
+ */
3955
+ var AuthAdministrationMissingError = class extends Error {
3956
+ constructor() {
3957
+ super("Initial grants include no reachable grant permitting execute on the auth scope: a policy with no creator must always include one");
3958
+ this.name = "AuthAdministrationMissingError";
3959
+ }
3960
+ };
3961
+ const GRANT_KEYS = new Set([
3962
+ "id",
3963
+ "description",
3964
+ "effect",
3965
+ "principal",
3966
+ "capability",
3967
+ "where"
3968
+ ]);
3969
+ const PRINCIPAL_KINDS = new Set([
3970
+ "anyone",
3971
+ "address",
3972
+ "group",
3973
+ "match"
3974
+ ]);
3975
+ const COMPARISON_CONDITION_KINDS = new Set([
3976
+ "eq",
3977
+ "ne",
3978
+ "lt",
3979
+ "lte",
3980
+ "gt",
3981
+ "gte"
3982
+ ]);
3983
+ function isPlainValue(value) {
3984
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3985
+ }
3986
+ function operandProblem(value, capabilityScope, budget) {
3987
+ budget.nodes -= 1;
3988
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
3989
+ if (!isPlainValue(value)) return "operand must be an object";
3990
+ const keys = Object.keys(value);
3991
+ if (keys.length !== 1) return "operand must have exactly one of attr or lit";
3992
+ if (keys[0] === "attr") {
3993
+ const attr = value.attr;
3994
+ if (typeof attr !== "string" || attr.length === 0) return "attr must be a non-empty string";
3995
+ if (capabilityScope !== void 0 && capabilityScope !== "*" && attr.startsWith("doc.")) {
3996
+ const pathScope = attr.split(".")[1] ?? "";
3997
+ if (pathScope !== capabilityScope) return `condition path "${attr}" reads scope "${pathScope}" but the capability covers only scope "${capabilityScope}"`;
3998
+ }
3999
+ return null;
4000
+ }
4001
+ if (keys[0] === "lit") {
4002
+ const lit = value.lit;
4003
+ if (lit !== null && typeof lit !== "string" && typeof lit !== "number" && typeof lit !== "boolean") return "lit must be a string, number, boolean, or null";
4004
+ if (typeof lit === "number" && !Number.isFinite(lit)) return "lit must be a finite number";
4005
+ if (typeof lit === "number" && Object.is(lit, -0)) return "lit must not be negative zero";
4006
+ return null;
4007
+ }
4008
+ return `unknown operand kind "${keys[0]}"`;
4009
+ }
4010
+ function conditionProblem(value, capabilityScope, depth, budget) {
4011
+ if (depth > 10) return `condition exceeds depth 10`;
4012
+ budget.nodes -= 1;
4013
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
4014
+ if (!isPlainValue(value)) return "condition must be an object";
4015
+ const keys = Object.keys(value);
4016
+ if (keys.length !== 1) return "condition must have exactly one operator";
4017
+ const kind = keys[0];
4018
+ const body = value[kind];
4019
+ if (COMPARISON_CONDITION_KINDS.has(kind)) {
4020
+ if (!Array.isArray(body) || body.length !== 2) return `${kind} requires a pair of operands`;
4021
+ for (const operand of body) {
4022
+ const problem = operandProblem(operand, capabilityScope, budget);
4023
+ if (problem !== null) return problem;
4024
+ }
4025
+ return null;
4026
+ }
4027
+ if (kind === "in" || kind === "notIn") {
4028
+ if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return `${kind} requires an operand and an operand list`;
4029
+ const first = operandProblem(body[0], capabilityScope, budget);
4030
+ if (first !== null) return first;
4031
+ for (const operand of body[1]) {
4032
+ const problem = operandProblem(operand, capabilityScope, budget);
4033
+ if (problem !== null) return problem;
4034
+ }
4035
+ return null;
4036
+ }
4037
+ if (kind === "exists") return operandProblem(body, capabilityScope, budget);
4038
+ if (kind === "and" || kind === "or") {
4039
+ if (!Array.isArray(body)) return `${kind} requires a condition list`;
4040
+ for (const child of body) {
4041
+ const problem = conditionProblem(child, capabilityScope, depth + 1, budget);
4042
+ if (problem !== null) return problem;
4043
+ }
4044
+ return null;
4045
+ }
4046
+ if (kind === "not") return conditionProblem(body, capabilityScope, depth + 1, budget);
4047
+ return `unknown condition operator "${kind}"`;
4048
+ }
4049
+ function principalProblem(value, capabilityScope) {
4050
+ if (!isPlainValue(value)) return "principal must be an object";
4051
+ const keys = Object.keys(value);
4052
+ if (keys.length !== 1 || !PRINCIPAL_KINDS.has(keys[0])) return "principal must have exactly one of anyone, address, group, or match";
4053
+ const kind = keys[0];
4054
+ if (kind === "anyone" && value.anyone !== true) return "anyone must be true";
4055
+ if (kind === "address") {
4056
+ const address = value.address;
4057
+ if (typeof address !== "string" || address.length === 0) return "address must be a non-empty string";
4058
+ }
4059
+ if (kind === "group") {
4060
+ const group = value.group;
4061
+ if (typeof group !== "string" || group.length === 0) return "group must be a non-empty string";
4062
+ }
4063
+ if (kind === "match") return conditionProblem(value.match, capabilityScope, 1, { nodes: 100 });
4064
+ return null;
4065
+ }
4066
+ function capabilityProblem(value) {
4067
+ if (!isPlainValue(value)) return "capability must be an object";
4068
+ const can = value.can;
4069
+ if (can !== "read" && can !== "execute") return "capability.can must be read or execute";
4070
+ const allowedKeys = can === "execute" ? [
4071
+ "can",
4072
+ "scope",
4073
+ "operation"
4074
+ ] : ["can", "scope"];
4075
+ const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key)).sort();
4076
+ if (unknownKeys.length > 0) return `unknown capability key "${unknownKeys[0]}"`;
4077
+ if (value.scope !== void 0) {
4078
+ if (typeof value.scope !== "string" || value.scope.length === 0) return "capability.scope must be a non-empty string";
4079
+ }
4080
+ if (can === "execute" && value.operation !== void 0) {
4081
+ const operation = value.operation;
4082
+ if (!Array.isArray(operation)) return "capability.operation must be an array";
4083
+ if (operation.length > 100) return `capability.operation exceeds 100 entries`;
4084
+ for (const entry of operation) if (typeof entry !== "string" || entry.length === 0) return "capability.operation entries must be non-empty strings";
4085
+ }
4086
+ return null;
4087
+ }
4088
+ /** Returns the first v1-rule violation, or null. Pure, total, deterministic. */
4089
+ function grantProblem(value) {
4090
+ if (!isPlainValue(value)) return "grant must be an object";
4091
+ const unknownKeys = Object.keys(value).filter((key) => !GRANT_KEYS.has(key)).sort();
4092
+ if (unknownKeys.length > 0) return `unknown grant key "${unknownKeys[0]}"`;
4093
+ if (typeof value.id !== "string" || value.id.length === 0) return "id must be a non-empty string";
4094
+ if (typeof value.description !== "string") return "description must be a string";
4095
+ if (value.effect !== "allow" && value.effect !== "deny") return "effect must be allow or deny";
4096
+ const capabilityValue = value.capability;
4097
+ const capability = capabilityProblem(capabilityValue);
4098
+ if (capability !== null) return capability;
4099
+ const capabilityScope = capabilityValue.scope;
4100
+ const principal = principalProblem(value.principal, capabilityScope);
4101
+ if (principal !== null) return principal;
4102
+ if (value.where !== void 0) return conditionProblem(value.where, capabilityScope, 1, { nodes: 100 });
4103
+ return null;
4104
+ }
4105
+ /** V1 shape rules plus the group-document group-principal ban. */
4106
+ function assertValidGrant(grant, documentType) {
4107
+ const grantId = isPlainValue(grant) && typeof grant.id === "string" ? grant.id : "";
4108
+ const problem = grantProblem(grant);
4109
+ if (problem !== null) throw new InvalidGrantError(grantId, problem);
4110
+ if (documentType === "@powerhousedao/document-group" && "group" in grant.principal) throw new GroupPrincipalNotAllowedError(grantId);
4111
+ }
4112
+ /**
4113
+ * Validates an initial grant list: the count cap, every grant, and — on a
4114
+ * creator-less policy — that some grant keeps the auth scope administrable.
4115
+ */
4116
+ function assertValidInitialGrants(grants, documentType, creator) {
4117
+ if (grants.length > 100) throw new InvalidGrantError("", `policy exceeds 100 grants`);
4118
+ for (const grant of grants) assertValidGrant(grant, documentType);
4119
+ if (creator === void 0 && !administrationReachable(grants)) throw new AuthAdministrationMissingError();
4120
+ }
4121
+ /**
4122
+ * Validates a grant upsert: the grant itself, the count cap on append, and
4123
+ * administration retention. Retention is checked on an append as well as an
4124
+ * in-place replace, because a grant appended after the administration grant can
4125
+ * shadow it (evaluation is last-applicable-grant-wins) and so take
4126
+ * administration away without removing anything.
4127
+ */
4128
+ function assertValidGrantUpsert(grant, existing, documentType, creator) {
4129
+ assertValidGrant(grant, documentType);
4130
+ const exists = existing.some((g) => g.id === grant.id);
4131
+ if (!exists && existing.length >= 100) throw new InvalidGrantError(grant.id, `policy exceeds 100 grants`);
4132
+ assertAuthAdministrationRetained(creator, existing, exists ? existing.map((g) => g.id === grant.id ? grant : g) : [...existing, grant], grant.id);
4133
+ }
4134
+ /**
4135
+ * The request whose coverage keeps a policy administrable: a subject who may
4136
+ * SET_GRANT can upsert any grant, so every other repair stays reachable.
4137
+ */
4138
+ const AUTH_ADMINISTRATION_REQUEST = {
4139
+ verb: "execute",
4140
+ scope: "auth",
4141
+ operation: "SET_GRANT"
4142
+ };
4143
+ /**
4144
+ * Whether some subject can still administer the auth scope under this grant list.
4145
+ *
4146
+ * Asks the evaluator rather than pattern-matching a single grant: evaluation is
4147
+ * last-applicable-grant-wins, so an allow that some later deny shadows keeps
4148
+ * nothing reachable even though it is still present in the list. Only anyone and
4149
+ * address principals are candidates, because those are the ones v1 can match at
4150
+ * all; a `where` condition or a group or match principal never applies.
4151
+ */
4152
+ function administrationReachable(grants) {
4153
+ return grants.some((grant) => {
4154
+ const subject = administrationCandidate(grant);
4155
+ if (subject === void 0) return false;
4156
+ return evaluateGrantStack(grants, subject, AUTH_ADMINISTRATION_REQUEST).decision === "allow";
4157
+ });
4158
+ }
4159
+ /**
4160
+ * The subject to test this grant with, or undefined when the grant could never
4161
+ * carry administration for anyone.
4162
+ */
4163
+ function administrationCandidate(grant) {
4164
+ if (grant.effect !== "allow" || grant.where !== void 0 || !capabilityCovers(grant.capability, AUTH_ADMINISTRATION_REQUEST)) return;
4165
+ if ("address" in grant.principal) return {
4166
+ address: grant.principal.address,
4167
+ key: void 0
4168
+ };
4169
+ if ("anyone" in grant.principal) return {
4170
+ address: void 0,
4171
+ key: void 0
4172
+ };
4173
+ }
4174
+ /**
4175
+ * A creator-less policy must always retain a grant permitting execute on the
4176
+ * auth scope; on a signed document the creator carve-out keeps administration
4177
+ * reachable instead. Rejects a change that takes the last such grant away. A
4178
+ * policy already without one is left alone: the change is not what locks it.
4179
+ */
4180
+ function assertAuthAdministrationRetained(creator, previous, next, grantId) {
4181
+ if (creator !== void 0) return;
4182
+ if (administrationReachable(previous) && !administrationReachable(next)) throw new AuthAdministrationLockoutError(grantId);
4183
+ }
4184
+ function capabilityCovers(capability, request) {
4185
+ if (capability.can !== request.verb) return false;
4186
+ const scope = capability.scope;
4187
+ if (scope !== void 0 && scope !== "*" && scope !== request.scope) return false;
4188
+ if (capability.can === "execute") {
4189
+ if (capability.operation === void 0) return true;
4190
+ return request.operation !== void 0 && capability.operation.includes(request.operation);
4191
+ }
4192
+ return true;
4193
+ }
4194
+ function principalMatches(principal, subject) {
4195
+ if ("anyone" in principal) return true;
4196
+ if ("address" in principal) return subject.address !== void 0 && subject.address.toLowerCase() === principal.address.toLowerCase();
4197
+ return false;
4198
+ }
4199
+ /**
4200
+ * Evaluates a v1 grant stack: default deny, last applicable grant wins, and
4201
+ * reports which grant decided it. Group and match principals and `where`
4202
+ * conditions are not evaluated yet; a grant that uses any of them never applies.
4203
+ */
4204
+ function evaluateGrantStack(grants, subject, request) {
4205
+ let applicable;
4206
+ for (const grant of grants) {
4207
+ if (grant.where !== void 0) continue;
4208
+ if (capabilityCovers(grant.capability, request) && principalMatches(grant.principal, subject)) applicable = grant;
4209
+ }
4210
+ if (applicable === void 0) return {
4211
+ decision: "deny",
4212
+ refusal: "no-applicable-grant"
4213
+ };
4214
+ if (applicable.effect === "deny") return {
4215
+ decision: "deny",
4216
+ refusal: "denied-by-grant",
4217
+ grantId: applicable.id
4218
+ };
4219
+ return { decision: "allow" };
4220
+ }
3728
4221
  const documentModelFileExtension = "phdm";
3729
4222
  const documentModelInitialLocalState = {};
3730
4223
  const documentModelInitialGlobalState = {
@@ -4306,7 +4799,306 @@ const documentModelGlobalState = {
4306
4799
  };
4307
4800
  const HASH_ALGORITHM_SHA1 = "sha1";
4308
4801
  const HASH_ENCODING_BASE64 = "base64";
4309
- const documentModelDocumentType = "powerhouse/document-model";
4802
+ /**
4803
+ * Creates a default PHAuthState
4804
+ */
4805
+ function defaultAuthState() {
4806
+ return {
4807
+ version: 0,
4808
+ grants: []
4809
+ };
4810
+ }
4811
+ /**
4812
+ * Creates a default PHDocumentState
4813
+ */
4814
+ function defaultDocumentState() {
4815
+ return {
4816
+ version: 0,
4817
+ hash: {
4818
+ algorithm: HASH_ALGORITHM_SHA1,
4819
+ encoding: HASH_ENCODING_BASE64
4820
+ }
4821
+ };
4822
+ }
4823
+ /**
4824
+ * Creates a default PHBaseState with auth and document properties
4825
+ */
4826
+ function defaultBaseState() {
4827
+ return {
4828
+ auth: defaultAuthState(),
4829
+ document: defaultDocumentState()
4830
+ };
4831
+ }
4832
+ /**
4833
+ * Creates a PHAuthState with the given properties
4834
+ */
4835
+ function createAuthState(auth) {
4836
+ return {
4837
+ ...defaultAuthState(),
4838
+ ...auth
4839
+ };
4840
+ }
4841
+ /**
4842
+ * Creates a PHDocumentState with the given properties
4843
+ */
4844
+ function createDocumentState(document) {
4845
+ return {
4846
+ ...defaultDocumentState(),
4847
+ ...document
4848
+ };
4849
+ }
4850
+ /**
4851
+ * Creates a PHBaseState with the given auth and document properties
4852
+ */
4853
+ function createBaseState(auth, document) {
4854
+ return {
4855
+ auth: createAuthState(auth),
4856
+ document: createDocumentState(document)
4857
+ };
4858
+ }
4859
+ /**
4860
+ * Backfills the auth scope to the default for legacy documents serialized with
4861
+ * an empty `auth`. Replaces only `state.auth`. Idempotent.
4862
+ */
4863
+ function backfillAuthState(state) {
4864
+ return {
4865
+ ...state,
4866
+ auth: createAuthState(state.auth)
4867
+ };
4868
+ }
4869
+ function defaultGlobalState() {
4870
+ return {
4871
+ ...defaultBaseState(),
4872
+ author: {
4873
+ name: "",
4874
+ website: ""
4875
+ },
4876
+ description: "",
4877
+ extension: "",
4878
+ id: "",
4879
+ name: "",
4880
+ specifications: []
4881
+ };
4882
+ }
4883
+ function defaultLocalState() {
4884
+ return {};
4885
+ }
4886
+ function createGlobalState(state) {
4887
+ return {
4888
+ ...defaultGlobalState(),
4889
+ ...state || {}
4890
+ };
4891
+ }
4892
+ function createLocalState(state) {
4893
+ return {
4894
+ ...defaultLocalState(),
4895
+ ...state || {}
4896
+ };
4897
+ }
4898
+ function createState(baseState, globalState, localState) {
4899
+ return {
4900
+ ...createBaseState(baseState?.auth, baseState?.document),
4901
+ global: createGlobalState(globalState),
4902
+ local: createLocalState(localState)
4903
+ };
4904
+ }
4905
+ /**
4906
+ * Destructuring a null input (reachable via raw synced operations) would
4907
+ * store an engine-specific TypeError message on the error operation.
4908
+ */
4909
+ function assertActionInputShape(input) {
4910
+ if (!isPlainValue(input)) throw new InvalidActionInputError({ input: "must be an object" });
4911
+ }
4912
+ function withGrants(document, grants) {
4913
+ return {
4914
+ ...document,
4915
+ state: {
4916
+ ...document.state,
4917
+ auth: {
4918
+ ...document.state.auth,
4919
+ grants
4920
+ }
4921
+ }
4922
+ };
4923
+ }
4924
+ const P256_PUBKEY_MULTICODEC = [128, 36];
4925
+ const DID_KEY_PREFIX = "did:key:z";
4926
+ function bytesEqual(a, b) {
4927
+ if (a.length !== b.length) return false;
4928
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
4929
+ return true;
4930
+ }
4931
+ /**
4932
+ * True when `signerKey` (an ActionSigner app key, a did:key) identifies the same
4933
+ * key recorded as the document creator. Returns false
4934
+ * when there is no creator (empty JWK) or no signer key.
4935
+ */
4936
+ function isDocumentCreator(creatorKey, signerKey) {
4937
+ if (!creatorKey?.x || !creatorKey.y) return false;
4938
+ if (!signerKey || !signerKey.startsWith(DID_KEY_PREFIX)) return false;
4939
+ const decoded = base58Decode(signerKey.slice(9));
4940
+ if (!decoded || decoded.length !== 35) return false;
4941
+ if (decoded[0] !== P256_PUBKEY_MULTICODEC[0] || decoded[1] !== P256_PUBKEY_MULTICODEC[1]) return false;
4942
+ const parityPrefix = decoded[2];
4943
+ if (parityPrefix !== 2 && parityPrefix !== 3) return false;
4944
+ const didX = decoded.subarray(3, 35);
4945
+ const jwkX = base64UrlToBytes(creatorKey.x);
4946
+ const jwkY = base64UrlToBytes(creatorKey.y);
4947
+ if (jwkX.length !== 32 || jwkY.length !== 32) return false;
4948
+ if (!bytesEqual(didX, jwkX)) return false;
4949
+ return (jwkY[31] & 1) === 1 === (parityPrefix === 3);
4950
+ }
4951
+ /**
4952
+ * Sets the initial policy. Valid only while the auth scope is uninitialized
4953
+ * (version 0). The input version is the policy language version and must be an
4954
+ * integer >= 1; 0 is reserved for the uninitialized state. On a signed-header
4955
+ * document it must be signed by the document creator (`header.sig.publicKey`).
4956
+ * A creator-less policy must include a grant permitting execute on the auth
4957
+ * scope, or it would be born locked out.
4958
+ */
4959
+ function applyInitializeAuthAction(document, action) {
4960
+ assertActionInputShape(action.input);
4961
+ const { version, grants } = action.input;
4962
+ if (!Number.isInteger(version) || version < 1) throw new InvalidAuthVersionError(document.header.id, version);
4963
+ if (document.state.auth.version !== 0) throw new AuthAlreadyInitializedError(document.header.id);
4964
+ if (!Array.isArray(grants)) throw new InvalidActionInputError({ grants: "must be an array" });
4965
+ const creatorKey = document.header.sig.publicKey;
4966
+ const signerKey = action.context?.signer?.app.key;
4967
+ const hasCreator = Boolean(creatorKey.kty || creatorKey.x || creatorKey.y);
4968
+ if (hasCreator && !isDocumentCreator(creatorKey, signerKey)) throw new AuthInitializerNotCreatorError(document.header.id);
4969
+ const creator = hasCreator ? signerKey : void 0;
4970
+ assertValidInitialGrants(grants, document.header.documentType, creator);
4971
+ return {
4972
+ ...document,
4973
+ state: {
4974
+ ...document.state,
4975
+ auth: createAuthState(creator ? {
4976
+ version,
4977
+ grants,
4978
+ creator
4979
+ } : {
4980
+ version,
4981
+ grants
4982
+ })
4983
+ }
4984
+ };
4985
+ }
4986
+ /** Upserts a grant by id: replaces in place if present, otherwise appends. */
4987
+ function applySetGrantAction(document, action) {
4988
+ assertActionInputShape(action.input);
4989
+ const { grant } = action.input;
4990
+ const grants = document.state.auth.grants;
4991
+ assertValidGrantUpsert(grant, grants, document.header.documentType, document.state.auth.creator);
4992
+ return withGrants(document, grants.some((g) => g.id === grant.id) ? grants.map((g) => g.id === grant.id ? grant : g) : [...grants, grant]);
4993
+ }
4994
+ /**
4995
+ * Removes a grant by id; throws if the id is not present or if the removal
4996
+ * would leave a creator-less policy with no auth-administration grant.
4997
+ */
4998
+ function applyRemoveGrantAction(document, action) {
4999
+ assertActionInputShape(action.input);
5000
+ const { id } = action.input;
5001
+ const { grants, creator } = document.state.auth;
5002
+ if (!grants.some((g) => g.id === id)) throw new GrantNotFoundError(id);
5003
+ const next = grants.filter((g) => g.id !== id);
5004
+ assertAuthAdministrationRetained(creator, grants, next, id);
5005
+ return withGrants(document, next);
5006
+ }
5007
+ /**
5008
+ * Moves a grant by id to a new index. Order is load-bearing (the last
5009
+ * applicable grant wins), so the relative order of the other grants is kept.
5010
+ * The target index is clamped to the valid range; an unknown id throws.
5011
+ *
5012
+ * Order alone decides which grant wins, so a move can take administration away
5013
+ * without changing the list's contents. It carries the same retention rule as
5014
+ * the two mutation paths.
5015
+ */
5016
+ function applyMoveGrantAction(document, action) {
5017
+ assertActionInputShape(action.input);
5018
+ const { id, index } = action.input;
5019
+ const { grants, creator } = document.state.auth;
5020
+ const from = grants.findIndex((g) => g.id === id);
5021
+ if (from === -1) throw new GrantNotFoundError(id);
5022
+ const next = [...grants];
5023
+ const [moved] = next.splice(from, 1);
5024
+ const to = Math.max(0, Math.min(index, next.length));
5025
+ next.splice(to, 0, moved);
5026
+ assertAuthAdministrationRetained(creator, grants, next, id);
5027
+ return withGrants(document, next);
5028
+ }
5029
+ /**
5030
+ * Dispatches an auth-scope action to its handler. This is the auth scope's
5031
+ * dedicated reducer: it is applied by the base reducer instead of the model
5032
+ * reducer, mirroring the document-scope platform handlers. Unknown types are a
5033
+ * no-op, matching the model-reducer default.
5034
+ */
5035
+ function applyAuthAction(document, action) {
5036
+ switch (action.type) {
5037
+ case "INITIALIZE_AUTH": return applyInitializeAuthAction(document, action);
5038
+ case "SET_GRANT": return applySetGrantAction(document, action);
5039
+ case "REMOVE_GRANT": return applyRemoveGrantAction(document, action);
5040
+ case "MOVE_GRANT": return applyMoveGrantAction(document, action);
5041
+ default: return document;
5042
+ }
5043
+ }
5044
+ /**
5045
+ * Because only creators can initialize auth scopes, we must verify that either
5046
+ * the document has no auth or the version and creator match.
5047
+ */
5048
+ function assertAuthPreservedOnDuplicate(documentId, source, duplicated) {
5049
+ if (!source || source.version === 0) return;
5050
+ if (duplicated === void 0 || duplicated.version !== source.version || duplicated.creator !== source.creator) throw new AuthPolicyNotPreservedError(documentId);
5051
+ }
5052
+ /**
5053
+ * The auth scope a state snapshot may install, given the policy already there.
5054
+ *
5055
+ * `applyAuthAction` is the validated door onto `state.auth`, but a whole-state
5056
+ * snapshot (UPGRADE_DOCUMENT's `initialState`, LOAD_STATE's `data`) replaces the
5057
+ * scope wholesale and is authorized as a `document`-scope write. Without this,
5058
+ * a subject holding `execute` on `document` and no auth grant at all can install
5059
+ * a policy of its choosing, name itself `creator` (which exempts the policy from
5060
+ * the retention rule for good), or wipe an existing policy by carrying the
5061
+ * default uninitialized one.
5062
+ *
5063
+ * Three cases:
5064
+ *
5065
+ * - the snapshot carries no policy, or an uninitialized one: the document
5066
+ * keeps the policy it has, so a default-state upgrade cannot reset it;
5067
+ * - the document is uninitialized and the snapshot carries a policy: the
5068
+ * policy is installed after the same validation genesis applies, so it
5069
+ * cannot be born locked out;
5070
+ * - both carry a policy: they must agree. A duplicate preserves its source's
5071
+ * policy (see {@link assertAuthPreservedOnDuplicate}), and anything else is
5072
+ * an attempt to replace one policy with another.
5073
+ */
5074
+ function resolveSnapshotAuth(documentId, documentType, current, incoming) {
5075
+ const currentAuth = current ?? createAuthState({
5076
+ version: 0,
5077
+ grants: []
5078
+ });
5079
+ if (!incoming || !incoming.version) return currentAuth;
5080
+ if (currentAuth.version !== 0) {
5081
+ if (stringify$1(incoming) !== stringify$1(currentAuth)) throw new AuthPolicyNotPreservedError(documentId);
5082
+ return incoming;
5083
+ }
5084
+ if (!Array.isArray(incoming.grants)) throw new InvalidActionInputError({ grants: "must be an array" });
5085
+ assertValidInitialGrants(incoming.grants, documentType, incoming.creator);
5086
+ return incoming;
5087
+ }
5088
+ /** UNDO, REDO and PRUNE are rejected on the auth scope. */
5089
+ function assertAuthScopeActionAllowed(action) {
5090
+ if (action.scope === "auth" && [
5091
+ "UNDO",
5092
+ "REDO",
5093
+ "PRUNE"
5094
+ ].includes(action.type)) throw new AuthActionNotAllowedError(action.type);
5095
+ }
5096
+ /**
5097
+ * True iff authorization rejected the action.
5098
+ */
5099
+ function isDenied(operation) {
5100
+ return operation.deniedReason !== void 0;
5101
+ }
4310
5102
  const BaseDocumentHeaderSchema = object({
4311
5103
  id: string(),
4312
5104
  name: string(),
@@ -4437,6 +5229,7 @@ function createDocumentScopeOperations(header, state) {
4437
5229
  function baseCreateDocument(createState, initialState, documentType = "") {
4438
5230
  const state = createState(initialState);
4439
5231
  const header = createPresignedHeader(generateId(), documentType);
5232
+ header.protocolVersions = { "base-reducer": 2 };
4440
5233
  return {
4441
5234
  header,
4442
5235
  state,
@@ -4460,6 +5253,18 @@ function hashDocumentStateForScope(document, scope = "global") {
4460
5253
  * Uses the V2 model where consecutive NOOPs form chains.
4461
5254
  * Unlike V1 garbageCollect, this preserves ALL operations but marks which to apply.
4462
5255
  */
5256
+ /**
5257
+ * The base-reducer protocol version a document is written against. Every
5258
+ * document carries one, set from the CREATE_DOCUMENT input. A header without
5259
+ * one was built outside that path, and choosing a version on its behalf would
5260
+ * replay the document through a reducer it was not written for, so it is an
5261
+ * error rather than a default.
5262
+ */
5263
+ function baseReducerVersion(header) {
5264
+ const version = header.protocolVersions?.["base-reducer"];
5265
+ if (typeof version !== "number") throw new Error(`Document ${header.id} carries no base-reducer protocol version`);
5266
+ return version;
5267
+ }
4463
5268
  function garbageCollectV2(sortedOperations) {
4464
5269
  const result = [];
4465
5270
  let noopChainLength = 0;
@@ -4476,9 +5281,26 @@ function garbageCollectV2(sortedOperations) {
4476
5281
  const defaultCreateState = (state) => {
4477
5282
  return state;
4478
5283
  };
5284
+ /**
5285
+ * Records an operation in the history without applying it, which is what a
5286
+ * denied operation needs: it occupies its index and contributes no state.
5287
+ *
5288
+ * The scope defaults to the action's own, and is passed explicitly by a rebuild
5289
+ * that is walking one stream and does not want to trust the action's copy.
5290
+ */
5291
+ function appendWithoutApplying(document, operation, scope = operation.action.scope) {
5292
+ return {
5293
+ ...document,
5294
+ operations: {
5295
+ ...document.operations,
5296
+ [scope]: [...document.operations[scope] ?? [], operation]
5297
+ }
5298
+ };
5299
+ }
4479
5300
  function replayDocument(initialState, operations, reducer, header, dispatch, skipHeaderOperations = {}, options) {
4480
5301
  const { checkHashes = true, reuseOperationResultingState, operationResultingStateParser = parseResultingState, skipIndexValidation } = options || {};
4481
- let documentState = initialState;
5302
+ const backfilledInitialState = backfillAuthState(initialState);
5303
+ let documentState = backfilledInitialState;
4482
5304
  const operationsToReplay = [];
4483
5305
  const allScopes = new Set([
4484
5306
  ...Object.keys(operations),
@@ -4513,12 +5335,13 @@ function replayDocument(initialState, operations, reducer, header, dispatch, ski
4513
5335
  const document = {
4514
5336
  header,
4515
5337
  state: defaultCreateState(documentState),
4516
- initialState,
5338
+ initialState: backfilledInitialState,
4517
5339
  operations: initialOperations,
4518
5340
  clipboard: []
4519
5341
  };
4520
5342
  let result = document;
4521
5343
  if (operationsToReplay.length) result = operationsToReplay.reduce((document, operation) => {
5344
+ if (isDenied(operation)) return appendWithoutApplying(document, operation);
4522
5345
  return reducer(document, operation.action, dispatch, {
4523
5346
  ignoreSkipOperations: true,
4524
5347
  checkHashes,
@@ -4800,13 +5623,15 @@ function redoOperation(document, action, skip) {
4800
5623
  });
4801
5624
  }
4802
5625
  function loadStateOperation(document, action) {
5626
+ const loaded = backfillAuthState(action.state.data);
5627
+ loaded.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, loaded.auth);
4803
5628
  return {
4804
5629
  ...document,
4805
5630
  header: {
4806
5631
  ...document.header,
4807
5632
  name: action.state.name
4808
5633
  },
4809
- state: action.state.data
5634
+ state: loaded
4810
5635
  };
4811
5636
  }
4812
5637
  function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
@@ -4980,12 +5805,13 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
4980
5805
  function baseReducer(document, action, customReducer, dispatch, options = {}) {
4981
5806
  const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main" } = options;
4982
5807
  let _action = actionFromAction(action);
5808
+ assertAuthScopeActionAllowed(_action);
4983
5809
  let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
4984
5810
  let newDocument = { ...document };
4985
5811
  let reuseLastOperationIndex = false;
4986
5812
  const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
4987
5813
  if (isUndoRedo(_action)) {
4988
- const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? 1);
5814
+ const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? baseReducerVersion(document.header));
4989
5815
  _action = transformedAction;
4990
5816
  skipValue = calculatedSkip;
4991
5817
  newDocument = processedDocument;
@@ -5001,7 +5827,7 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5001
5827
  branch
5002
5828
  };
5003
5829
  newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
5004
- const protocolVersion = options.protocolVersion ?? 1;
5830
+ const protocolVersion = options.protocolVersion ?? baseReducerVersion(document.header);
5005
5831
  if (isUndo(action) && protocolVersion < 2) return processUndoOperation(newDocument, action.scope, customReducer);
5006
5832
  const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
5007
5833
  if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
@@ -5027,6 +5853,13 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5027
5853
  }
5028
5854
  newDocument = create(newDocument, (draft) => {
5029
5855
  try {
5856
+ if (_action.scope === "auth") {
5857
+ const authState = applyAuthAction(newDocument, _action).state;
5858
+ unsafe(() => {
5859
+ draft.state = castDraft(authState);
5860
+ });
5861
+ return;
5862
+ }
5030
5863
  const newState = customReducer(draft.state, _action, dispatch);
5031
5864
  if (newState) unsafe(() => {
5032
5865
  draft.state = castDraft(newState);
@@ -5820,10 +6653,12 @@ function applyInitialState(document, action) {
5820
6653
  const input = action.input;
5821
6654
  const newState = input.initialState || input.state;
5822
6655
  if (newState) {
5823
- document.state = {
6656
+ const merged = backfillAuthState({
5824
6657
  ...document.state,
5825
6658
  ...newState
5826
- };
6659
+ });
6660
+ merged.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, merged.auth);
6661
+ document.state = merged;
5827
6662
  document.initialState = document.state;
5828
6663
  }
5829
6664
  }
@@ -5883,7 +6718,6 @@ function computeUpgradeTransitions(manifest, fromVersion, toVersion) {
5883
6718
  }
5884
6719
  return transitions;
5885
6720
  }
5886
- const NON_DOMAIN_SCOPES$1 = new Set(["auth", "document"]);
5887
6721
  function highestReducerVersion(reducers) {
5888
6722
  const keys = Object.keys(reducers).map(Number);
5889
6723
  if (keys.length === 0) throw new Error("VersionedReplayConfig.reducers must not be empty");
@@ -5911,15 +6745,15 @@ function highestReducerVersion(reducers) {
5911
6745
  */
5912
6746
  function replayDocumentVersioned(initialState, operations, config, header, dispatch, options) {
5913
6747
  const { checkHashes = true, skipIndexValidation } = options || {};
5914
- const protocolVersion = header.protocolVersions?.["base-reducer"] ?? 1;
6748
+ const protocolVersion = baseReducerVersion(header);
5915
6749
  const spine = (operations["document"] ?? []).slice().sort((a, b) => a.index - b.index);
5916
6750
  const upgrades = spine.filter((op) => op.action.type === "UPGRADE_DOCUMENT");
5917
6751
  const legacyFallback = () => {
5918
- const domainOps = Object.fromEntries(Object.entries(operations).filter(([s]) => !NON_DOMAIN_SCOPES$1.has(s)));
6752
+ const replayOps = Object.fromEntries(Object.entries(operations).filter(([s]) => s !== "document"));
5919
6753
  const latestVersion = highestReducerVersion(config.reducers);
5920
6754
  const reducer = config.reducers[latestVersion];
5921
6755
  return {
5922
- ...replayDocument(initialState, domainOps, reducer, header, dispatch, {}, options),
6756
+ ...replayDocument(initialState, replayOps, reducer, header, dispatch, {}, options),
5923
6757
  operations
5924
6758
  };
5925
6759
  };
@@ -5943,14 +6777,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5943
6777
  const a = op.action;
5944
6778
  return a.input.fromVersion > 0 && a.input.fromVersion < a.input.toVersion;
5945
6779
  });
5946
- const domainScopes = Object.keys(operations).filter((s) => !NON_DOMAIN_SCOPES$1.has(s));
6780
+ const replayScopes = Object.keys(operations).filter((s) => s !== "document");
5947
6781
  const scopeOps = {};
5948
- for (const s of domainScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
6782
+ for (const s of replayScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
5949
6783
  const boundaries = validatedUpgrades.map((upgradeOp) => {
5950
6784
  const revisionSnapshot = upgradeOp.action.input.revision;
5951
6785
  const upgradeTimestamp = upgradeOp.timestampUtcMs;
5952
6786
  const boundary = {};
5953
- for (const s of domainScopes) {
6787
+ for (const s of replayScopes) {
5954
6788
  const ops = scopeOps[s] ?? [];
5955
6789
  if (revisionSnapshot !== void 0) {
5956
6790
  const rev = revisionSnapshot[s] ?? 0;
@@ -5968,7 +6802,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5968
6802
  }
5969
6803
  return boundary;
5970
6804
  });
5971
- for (let i = 1; i < boundaries.length; i++) for (const s of domainScopes) {
6805
+ for (let i = 1; i < boundaries.length; i++) for (const s of replayScopes) {
5972
6806
  const prev = boundaries[i - 1]?.[s] ?? 0;
5973
6807
  const curr = boundaries[i]?.[s] ?? 0;
5974
6808
  if (boundaries[i]) boundaries[i][s] = Math.max(prev, curr);
@@ -5980,10 +6814,11 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5980
6814
  ]);
5981
6815
  const initialOperations = {};
5982
6816
  for (const s of allScopes) initialOperations[s] = [];
6817
+ const backfilledSeed = backfillAuthState(seedState);
5983
6818
  let document = {
5984
6819
  header,
5985
- state: seedState,
5986
- initialState: seedState,
6820
+ state: backfilledSeed,
6821
+ initialState: backfilledSeed,
5987
6822
  operations: initialOperations,
5988
6823
  clipboard: []
5989
6824
  };
@@ -5995,13 +6830,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5995
6830
  const available = Object.keys(config.reducers).join(", ");
5996
6831
  throw new Error(`No reducer registered for document version ${currentVersion}. Available versions: ${available}`);
5997
6832
  }
5998
- for (const s of domainScopes) {
6833
+ for (const s of replayScopes) {
5999
6834
  const ops = scopeOps[s] ?? [];
6000
6835
  const segStart = k === 0 ? 0 : boundaries[k - 1]?.[s] ?? 0;
6001
6836
  const segEnd = k < validatedUpgrades.length ? boundaries[k]?.[s] ?? ops.length : ops.length;
6002
6837
  const segOps = ops.slice(segStart, segEnd);
6003
6838
  for (const op of segOps) {
6004
- document = reducer(document, op.action, dispatch, {
6839
+ if (isDenied(op)) document = appendWithoutApplying(document, op, s);
6840
+ else document = reducer(document, op.action, dispatch, {
6005
6841
  ignoreSkipOperations: true,
6006
6842
  checkHashes,
6007
6843
  skipIndexValidation,
@@ -6018,6 +6854,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6018
6854
  if (!spineOp) continue;
6019
6855
  const spineActionType = spineOp.action.type;
6020
6856
  if (spineActionType === "CREATE_DOCUMENT" || spineActionType === "UPGRADE_DOCUMENT") continue;
6857
+ if (isDenied(spineOp)) continue;
6021
6858
  if (spineActionType === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, spineOp.action);
6022
6859
  else document = reducer(document, spineOp.action, dispatch, {
6023
6860
  ignoreSkipOperations: true,
@@ -6048,7 +6885,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6048
6885
  }
6049
6886
  };
6050
6887
  if (!checkHashes) {
6051
- const allReplayedOps = domainScopes.flatMap((s) => scopeOps[s] ?? []);
6888
+ const allReplayedOps = replayScopes.flatMap((s) => scopeOps[s] ?? []);
6052
6889
  for (const scope of Object.keys(document.state)) {
6053
6890
  const capturedHash = segmentEndHashPerScope.get(scope);
6054
6891
  const scopeHash = capturedHash !== void 0 ? capturedHash : hashDocumentStateForScope(document, scope);
@@ -6090,7 +6927,6 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6090
6927
  }
6091
6928
  };
6092
6929
  }
6093
- const NON_DOMAIN_SCOPES = new Set(["auth", "document"]);
6094
6930
  function zipAsync(data) {
6095
6931
  return new Promise((resolve, reject) => {
6096
6932
  zip(data, (err, out) => err ? reject(err) : resolve(out));
@@ -6169,7 +7005,7 @@ async function parseZipData(data) {
6169
7005
  async function loadFromZipData(data, reducer, options) {
6170
7006
  const { initialState, header, clearedOperations } = await parseZipData(data);
6171
7007
  return {
6172
- ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => !NON_DOMAIN_SCOPES.has(scope))), reducer, header, void 0, {}, options),
7008
+ ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => scope !== "document")), reducer, header, void 0, {}, options),
6173
7009
  operations: clearedOperations
6174
7010
  };
6175
7011
  }
@@ -6192,96 +7028,6 @@ const documentModelLoadFromInput = (input) => {
6192
7028
  const documentModelSaveToFileHandle = (document, input) => {
6193
7029
  return baseSaveToFileHandle(document, input);
6194
7030
  };
6195
- /**
6196
- * Creates a default PHAuthState
6197
- */
6198
- function defaultAuthState() {
6199
- return {};
6200
- }
6201
- /**
6202
- * Creates a default PHDocumentState
6203
- */
6204
- function defaultDocumentState() {
6205
- return {
6206
- version: 0,
6207
- hash: {
6208
- algorithm: HASH_ALGORITHM_SHA1,
6209
- encoding: HASH_ENCODING_BASE64
6210
- }
6211
- };
6212
- }
6213
- /**
6214
- * Creates a default PHBaseState with auth and document properties
6215
- */
6216
- function defaultBaseState() {
6217
- return {
6218
- auth: defaultAuthState(),
6219
- document: defaultDocumentState()
6220
- };
6221
- }
6222
- /**
6223
- * Creates a PHAuthState with the given properties
6224
- */
6225
- function createAuthState(auth) {
6226
- return {
6227
- ...defaultAuthState(),
6228
- ...auth
6229
- };
6230
- }
6231
- /**
6232
- * Creates a PHDocumentState with the given properties
6233
- */
6234
- function createDocumentState(document) {
6235
- return {
6236
- ...defaultDocumentState(),
6237
- ...document
6238
- };
6239
- }
6240
- /**
6241
- * Creates a PHBaseState with the given auth and document properties
6242
- */
6243
- function createBaseState(auth, document) {
6244
- return {
6245
- auth: createAuthState(auth),
6246
- document: createDocumentState(document)
6247
- };
6248
- }
6249
- function defaultGlobalState() {
6250
- return {
6251
- ...defaultBaseState(),
6252
- author: {
6253
- name: "",
6254
- website: ""
6255
- },
6256
- description: "",
6257
- extension: "",
6258
- id: "",
6259
- name: "",
6260
- specifications: []
6261
- };
6262
- }
6263
- function defaultLocalState() {
6264
- return {};
6265
- }
6266
- function createGlobalState(state) {
6267
- return {
6268
- ...defaultGlobalState(),
6269
- ...state || {}
6270
- };
6271
- }
6272
- function createLocalState(state) {
6273
- return {
6274
- ...defaultLocalState(),
6275
- ...state || {}
6276
- };
6277
- }
6278
- function createState(baseState, globalState, localState) {
6279
- return {
6280
- ...createBaseState(baseState?.auth, baseState?.document),
6281
- global: createGlobalState(globalState),
6282
- local: createLocalState(localState)
6283
- };
6284
- }
6285
7031
  //#endregion
6286
7032
  //#region ../document-model/dist/index.js
6287
7033
  const isStateOfType$1 = (state) => {
@@ -7360,6 +8106,6 @@ const driveDocumentModelModule = {
7360
8106
  }
7361
8107
  };
7362
8108
  //#endregion
7363
- export { redo as A, setName as B, createZip as C, deleteOperationError as D, deleteOperation as E, setModelDescription as F, setStateSchema as G, setOperationErrorName as H, setModelExtension as I, validateModules as J, undo as K, setModelId as L, setAuthorName as M, setAuthorWebsite as N, documentModelDocumentType as O, setInitialState as P, setModelName as R, createState as S, deleteModule as T, setOperationName as U, setOperationDescription as V, setOperationSchema as W, generateId as X, validateStateSchemaName as Y, baseLoadFromInputVersioned as _, handleTargetNameCollisions as a, createPresignedHeader as b, moveNode as c, logger as d, addModule as f, baseLoadFromInput as g, baseCreateDocument as h, generateNodesCopy as i, replayDocumentVersioned as j, isDocumentAction as k, updateNode as l, addOperationError as m, copyNode as n, isFileNode as o, addOperation as p, validateInitialState as q, driveDocumentModelModule as r, isFolderNode as s, addFolder as t, documentModelDocumentModelModule as u, baseSaveToFileHandle as v, defaultBaseState as w, createReducer as x, buildSignedAction as y, setModuleName as z };
8109
+ export { generateId as $, documentModelDocumentType as A, setModelId as B, createReducer as C, deleteModule as D, defaultBaseState as E, setAuthorName as F, setOperationErrorName as G, setModuleName as H, setAuthorWebsite as I, setStateSchema as J, setOperationName as K, setInitialState as L, isDocumentAction as M, redo as N, deleteOperation as O, replayDocumentVersioned as P, validateStateSchemaName as Q, setModelDescription as R, createPresignedHeader as S, createZip as T, setName as U, setModelName as V, setOperationDescription as W, validateInitialState as X, undo as Y, validateModules as Z, baseCreateDocument as _, handleTargetNameCollisions as a, baseSaveToFileHandle as b, isValidName as c, documentModelDocumentModelModule as d, logger as f, assertAuthPreservedOnDuplicate as g, addOperationError as h, generateNodesCopy as i, hashDocumentStateForScope as j, deleteOperationError as k, moveNode as l, addOperation as m, copyNode as n, isFileNode as o, addModule as p, setOperationSchema as q, driveDocumentModelModule as r, isFolderNode as s, addFolder as t, updateNode as u, baseLoadFromInput as v, createState as w, buildSignedAction as x, baseLoadFromInputVersioned as y, setModelExtension as z };
7364
8110
 
7365
- //# sourceMappingURL=document-drive-sTKoi6L4.js.map
8111
+ //# sourceMappingURL=document-drive-D3Z9vXTQ.js.map