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

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, j 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-fA4EItr0.mjs";
1
+ import { D as __commonJSMin, M as __toESM, 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-DT7jI9ol.mjs";
2
2
  import { createRequire } from "module";
3
3
  //#endregion
4
4
  //#region ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
@@ -590,7 +590,7 @@ function isBE() {
590
590
  return new Uint8Array(new Uint16Array([65279]).buffer)[0] === 254;
591
591
  }
592
592
  //#endregion
593
- //#region ../shared/dist/utils-DKOFCM0Q.js
593
+ //#region ../shared/dist/utils-CTt1XRn-.js
594
594
  const hashBrowser = (data, algorithm = "sha1", encoding = "base64", _params) => {
595
595
  if (!["sha1"].includes(algorithm)) throw new Error(`Hashing algorithm not supported: "${algorithm}". Available: sha1`);
596
596
  if (!["base64", "hex"].includes(encoding)) throw new Error(`Hash encoding not supported: "${encoding}". Available: base64, hex`);
@@ -637,6 +637,40 @@ function ab2hex(ab) {
637
637
  const view = ArrayBuffer.isView(ab) ? ab : new Uint8Array(ab);
638
638
  return Array.prototype.map.call(view, (x) => ("00" + x.toString(16)).slice(-2)).join("");
639
639
  }
640
+ const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
641
+ /**
642
+ * Decodes a base58btc (Bitcoin/IPFS alphabet) string to bytes. Returns null on
643
+ * any character outside the alphabet.
644
+ */
645
+ function base58Decode(input) {
646
+ const bytes = [];
647
+ for (const ch of input) {
648
+ let carry = BASE58_ALPHABET.indexOf(ch);
649
+ if (carry === -1) return null;
650
+ for (let i = 0; i < bytes.length; i++) {
651
+ carry += bytes[i] * 58;
652
+ bytes[i] = carry & 255;
653
+ carry >>= 8;
654
+ }
655
+ while (carry > 0) {
656
+ bytes.push(carry & 255);
657
+ carry >>= 8;
658
+ }
659
+ }
660
+ for (let k = 0; k < input.length && input[k] === "1"; k++) bytes.push(0);
661
+ return Uint8Array.from(bytes.reverse());
662
+ }
663
+ /** Decodes a base64url string to bytes (portable across node, browsers, workers). */
664
+ function base64UrlToBytes(input) {
665
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
666
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
667
+ let binary;
668
+ if (typeof atob === "function") binary = atob(padded);
669
+ else binary = Buffer.from(padded, "base64").toString("binary");
670
+ const bytes = new Uint8Array(binary.length);
671
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
672
+ return bytes;
673
+ }
640
674
  function generateId(method) {
641
675
  if (method && method.toString() !== "UUIDv4") throw new Error(`Id generation method not supported: "${method.toString()}"`);
642
676
  return globalThis.crypto.randomUUID();
@@ -2995,6 +3029,75 @@ var DowngradeNotSupportedError = class extends Error {
2995
3029
  this.toVersion = toVersion;
2996
3030
  }
2997
3031
  };
3032
+ /**
3033
+ * Thrown when INITIALIZE_AUTH is applied to an already-initialized auth scope.
3034
+ * The genesis action is valid only at auth revision zero.
3035
+ */
3036
+ var AuthAlreadyInitializedError = class extends Error {
3037
+ documentId;
3038
+ constructor(documentId) {
3039
+ super(`Auth scope already initialized for document ${documentId}: INITIALIZE_AUTH is valid only at auth revision zero`);
3040
+ this.name = "AuthAlreadyInitializedError";
3041
+ this.documentId = documentId;
3042
+ }
3043
+ };
3044
+ /**
3045
+ * Thrown when INITIALIZE_AUTH is not signed by the document creator (its signer
3046
+ * does not match `header.sig.publicKey`), so it cannot set the auth policy.
3047
+ */
3048
+ var AuthInitializerNotCreatorError = class extends Error {
3049
+ documentId;
3050
+ constructor(documentId) {
3051
+ super(`INITIALIZE_AUTH for document ${documentId} must be signed by the document creator`);
3052
+ this.name = "AuthInitializerNotCreatorError";
3053
+ this.documentId = documentId;
3054
+ }
3055
+ };
3056
+ /**
3057
+ * Thrown when INITIALIZE_AUTH carries a version below 1. Version 0 is reserved
3058
+ * for the uninitialized auth scope.
3059
+ */
3060
+ var InvalidAuthVersionError = class extends Error {
3061
+ documentId;
3062
+ version;
3063
+ constructor(documentId, version) {
3064
+ super(`Invalid auth policy version ${version} for document ${documentId}: INITIALIZE_AUTH requires an integer version >= 1`);
3065
+ this.name = "InvalidAuthVersionError";
3066
+ this.documentId = documentId;
3067
+ this.version = version;
3068
+ }
3069
+ };
3070
+ /** Thrown when a duplicate would lose the source policy's version or creator binding. */
3071
+ var AuthPolicyNotPreservedError = class extends Error {
3072
+ documentId;
3073
+ constructor(documentId) {
3074
+ super(`Duplicating document ${documentId} would not preserve its auth policy: the copy loses the policy version or its creator binding`);
3075
+ this.name = "AuthPolicyNotPreservedError";
3076
+ this.documentId = documentId;
3077
+ }
3078
+ };
3079
+ /**
3080
+ * Thrown when a grant referenced by REMOVE_GRANT or MOVE_GRANT does not exist.
3081
+ */
3082
+ var GrantNotFoundError = class extends Error {
3083
+ grantId;
3084
+ constructor(grantId) {
3085
+ super(`Grant not found in auth scope: ${grantId}`);
3086
+ this.name = "GrantNotFoundError";
3087
+ this.grantId = grantId;
3088
+ }
3089
+ };
3090
+ /**
3091
+ * Thrown when a disallowed action (UNDO, REDO, PRUNE) targets the auth scope.
3092
+ */
3093
+ var AuthActionNotAllowedError = class extends Error {
3094
+ actionType;
3095
+ constructor(actionType) {
3096
+ super(`${actionType} is not permitted on the auth scope`);
3097
+ this.name = "AuthActionNotAllowedError";
3098
+ this.actionType = actionType;
3099
+ }
3100
+ };
2998
3101
  var HashMismatchError = class extends Error {
2999
3102
  _scope;
3000
3103
  _document;
@@ -3461,17 +3564,101 @@ const ConfigEntrySchema = object({
3461
3564
  required: boolean().optional(),
3462
3565
  default: boolean().optional()
3463
3566
  });
3567
+ const PwaUrlPatternSchema = union([string(), strictObject({
3568
+ source: string(),
3569
+ flags: string().optional()
3570
+ }).refine((p) => {
3571
+ try {
3572
+ new RegExp(p.source, p.flags);
3573
+ return true;
3574
+ } catch {
3575
+ return false;
3576
+ }
3577
+ }, { message: "source/flags do not compile to a valid RegExp" })]);
3578
+ const PwaRuntimeCachingSchema = strictObject({
3579
+ urlPattern: PwaUrlPatternSchema,
3580
+ handler: _enum([
3581
+ "CacheFirst",
3582
+ "CacheOnly",
3583
+ "NetworkFirst",
3584
+ "NetworkOnly",
3585
+ "StaleWhileRevalidate"
3586
+ ]),
3587
+ method: _enum([
3588
+ "GET",
3589
+ "POST",
3590
+ "PUT",
3591
+ "DELETE",
3592
+ "HEAD",
3593
+ "PATCH"
3594
+ ]).optional(),
3595
+ options: strictObject({
3596
+ cacheName: string().optional(),
3597
+ networkTimeoutSeconds: number().optional(),
3598
+ expiration: strictObject({
3599
+ maxEntries: number().optional(),
3600
+ maxAgeSeconds: number().optional()
3601
+ }).optional(),
3602
+ cacheableResponse: strictObject({
3603
+ statuses: array(number()).optional(),
3604
+ headers: record(string(), string()).optional()
3605
+ }).optional()
3606
+ }).optional()
3607
+ });
3608
+ const PwaIconSchema = strictObject({
3609
+ src: string(),
3610
+ sizes: string().optional(),
3611
+ type: string().optional(),
3612
+ purpose: string().optional()
3613
+ });
3614
+ const PwaFileHandlerSchema = strictObject({
3615
+ accept: record(string(), array(string().regex(/^\./, "file extensions must start with '.'"))),
3616
+ icons: array(PwaIconSchema).optional(),
3617
+ launch_type: _enum(["single-client", "multiple-clients"]).optional()
3618
+ });
3619
+ const PwaConfigSchema = strictObject({
3620
+ manifest: strictObject({
3621
+ name: string().optional(),
3622
+ short_name: string().optional(),
3623
+ description: string().optional(),
3624
+ theme_color: string().optional(),
3625
+ background_color: string().optional(),
3626
+ display: _enum([
3627
+ "fullscreen",
3628
+ "standalone",
3629
+ "minimal-ui",
3630
+ "browser"
3631
+ ]).optional(),
3632
+ start_url: string().optional(),
3633
+ scope: string().optional(),
3634
+ icons: array(PwaIconSchema).optional(),
3635
+ file_handlers: array(PwaFileHandlerSchema).optional(),
3636
+ launch_handler: strictObject({ client_mode: _enum([
3637
+ "auto",
3638
+ "focus-existing",
3639
+ "navigate-existing",
3640
+ "navigate-new"
3641
+ ]) }).optional()
3642
+ }).optional(),
3643
+ globPatterns: array(string()).optional(),
3644
+ globIgnores: array(string()).optional(),
3645
+ maximumFileSizeToCacheInBytes: number().optional(),
3646
+ runtimeCaching: array(PwaRuntimeCachingSchema).optional(),
3647
+ navigateFallbackDenylist: array(PwaUrlPatternSchema).optional()
3648
+ });
3464
3649
  object({
3465
3650
  name: string(),
3466
3651
  description: string().optional(),
3467
3652
  category: string().optional(),
3653
+ image: string().optional(),
3468
3654
  publisher: PublisherSchema.optional(),
3469
3655
  documentModels: PowerhouseModulesSchema,
3470
3656
  apps: PowerhouseModulesSchema,
3471
3657
  editors: PowerhouseModulesSchema,
3472
3658
  processors: PowerhouseModulesSchema,
3473
3659
  subgraphs: PowerhouseModulesSchema,
3474
- config: array(ConfigEntrySchema).optional()
3660
+ config: array(ConfigEntrySchema).optional(),
3661
+ pwa: PwaConfigSchema.optional()
3475
3662
  });
3476
3663
  /**
3477
3664
  * Cancels the last `count` operations.
@@ -3742,6 +3929,312 @@ const actions = {
3742
3929
  ...baseActions,
3743
3930
  ...documentModelActions
3744
3931
  };
3932
+ const documentModelDocumentType = "powerhouse/document-model";
3933
+ /**
3934
+ * Thrown when a grant violates the v1 validation rules. The message is stored
3935
+ * on error operations, so it must be a pure function of the input.
3936
+ */
3937
+ var InvalidGrantError = class extends Error {
3938
+ grantId;
3939
+ constructor(grantId, problem) {
3940
+ super(`Invalid grant "${grantId}": ${problem}`);
3941
+ this.name = "InvalidGrantError";
3942
+ this.grantId = grantId;
3943
+ }
3944
+ };
3945
+ /** Thrown for a `{ group }` principal on a group document: references never chain. */
3946
+ var GroupPrincipalNotAllowedError = class extends Error {
3947
+ grantId;
3948
+ constructor(grantId) {
3949
+ super(`Grant "${grantId}" uses a group principal on a group document: a group's auth scope cannot reference other groups`);
3950
+ this.name = "GroupPrincipalNotAllowedError";
3951
+ this.grantId = grantId;
3952
+ }
3953
+ };
3954
+ /**
3955
+ * Thrown when a change would leave a creator-less policy with no grant
3956
+ * permitting execute on the auth scope. Without the creator carve-out no
3957
+ * subject could ever administer such a policy again.
3958
+ */
3959
+ var AuthAdministrationLockoutError = class extends Error {
3960
+ grantId;
3961
+ constructor(grantId) {
3962
+ 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`);
3963
+ this.name = "AuthAdministrationLockoutError";
3964
+ this.grantId = grantId;
3965
+ }
3966
+ };
3967
+ /**
3968
+ * Thrown when INITIALIZE_AUTH would create a creator-less policy with no grant
3969
+ * permitting execute on the auth scope. Such a policy would be born with no
3970
+ * subject able to administer it.
3971
+ */
3972
+ var AuthAdministrationMissingError = class extends Error {
3973
+ constructor() {
3974
+ super("Initial grants include no reachable grant permitting execute on the auth scope: a policy with no creator must always include one");
3975
+ this.name = "AuthAdministrationMissingError";
3976
+ }
3977
+ };
3978
+ const GRANT_KEYS = new Set([
3979
+ "id",
3980
+ "description",
3981
+ "effect",
3982
+ "principal",
3983
+ "capability",
3984
+ "where"
3985
+ ]);
3986
+ const PRINCIPAL_KINDS = new Set([
3987
+ "anyone",
3988
+ "address",
3989
+ "group",
3990
+ "match"
3991
+ ]);
3992
+ const COMPARISON_CONDITION_KINDS = new Set([
3993
+ "eq",
3994
+ "ne",
3995
+ "lt",
3996
+ "lte",
3997
+ "gt",
3998
+ "gte"
3999
+ ]);
4000
+ function isPlainValue(value) {
4001
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4002
+ }
4003
+ function operandProblem(value, capabilityScope, budget) {
4004
+ budget.nodes -= 1;
4005
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
4006
+ if (!isPlainValue(value)) return "operand must be an object";
4007
+ const keys = Object.keys(value);
4008
+ if (keys.length !== 1) return "operand must have exactly one of attr or lit";
4009
+ if (keys[0] === "attr") {
4010
+ const attr = value.attr;
4011
+ if (typeof attr !== "string" || attr.length === 0) return "attr must be a non-empty string";
4012
+ if (capabilityScope !== void 0 && capabilityScope !== "*" && attr.startsWith("doc.")) {
4013
+ const pathScope = attr.split(".")[1] ?? "";
4014
+ if (pathScope !== capabilityScope) return `condition path "${attr}" reads scope "${pathScope}" but the capability covers only scope "${capabilityScope}"`;
4015
+ }
4016
+ return null;
4017
+ }
4018
+ if (keys[0] === "lit") {
4019
+ const lit = value.lit;
4020
+ if (lit !== null && typeof lit !== "string" && typeof lit !== "number" && typeof lit !== "boolean") return "lit must be a string, number, boolean, or null";
4021
+ if (typeof lit === "number" && !Number.isFinite(lit)) return "lit must be a finite number";
4022
+ if (typeof lit === "number" && Object.is(lit, -0)) return "lit must not be negative zero";
4023
+ return null;
4024
+ }
4025
+ return `unknown operand kind "${keys[0]}"`;
4026
+ }
4027
+ function conditionProblem(value, capabilityScope, depth, budget) {
4028
+ if (depth > 10) return `condition exceeds depth 10`;
4029
+ budget.nodes -= 1;
4030
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
4031
+ if (!isPlainValue(value)) return "condition must be an object";
4032
+ const keys = Object.keys(value);
4033
+ if (keys.length !== 1) return "condition must have exactly one operator";
4034
+ const kind = keys[0];
4035
+ const body = value[kind];
4036
+ if (COMPARISON_CONDITION_KINDS.has(kind)) {
4037
+ if (!Array.isArray(body) || body.length !== 2) return `${kind} requires a pair of operands`;
4038
+ for (const operand of body) {
4039
+ const problem = operandProblem(operand, capabilityScope, budget);
4040
+ if (problem !== null) return problem;
4041
+ }
4042
+ return null;
4043
+ }
4044
+ if (kind === "in" || kind === "notIn") {
4045
+ if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return `${kind} requires an operand and an operand list`;
4046
+ const first = operandProblem(body[0], capabilityScope, budget);
4047
+ if (first !== null) return first;
4048
+ for (const operand of body[1]) {
4049
+ const problem = operandProblem(operand, capabilityScope, budget);
4050
+ if (problem !== null) return problem;
4051
+ }
4052
+ return null;
4053
+ }
4054
+ if (kind === "exists") return operandProblem(body, capabilityScope, budget);
4055
+ if (kind === "and" || kind === "or") {
4056
+ if (!Array.isArray(body)) return `${kind} requires a condition list`;
4057
+ for (const child of body) {
4058
+ const problem = conditionProblem(child, capabilityScope, depth + 1, budget);
4059
+ if (problem !== null) return problem;
4060
+ }
4061
+ return null;
4062
+ }
4063
+ if (kind === "not") return conditionProblem(body, capabilityScope, depth + 1, budget);
4064
+ return `unknown condition operator "${kind}"`;
4065
+ }
4066
+ function principalProblem(value, capabilityScope) {
4067
+ if (!isPlainValue(value)) return "principal must be an object";
4068
+ const keys = Object.keys(value);
4069
+ if (keys.length !== 1 || !PRINCIPAL_KINDS.has(keys[0])) return "principal must have exactly one of anyone, address, group, or match";
4070
+ const kind = keys[0];
4071
+ if (kind === "anyone" && value.anyone !== true) return "anyone must be true";
4072
+ if (kind === "address") {
4073
+ const address = value.address;
4074
+ if (typeof address !== "string" || address.length === 0) return "address must be a non-empty string";
4075
+ }
4076
+ if (kind === "group") {
4077
+ const group = value.group;
4078
+ if (typeof group !== "string" || group.length === 0) return "group must be a non-empty string";
4079
+ }
4080
+ if (kind === "match") return conditionProblem(value.match, capabilityScope, 1, { nodes: 100 });
4081
+ return null;
4082
+ }
4083
+ function capabilityProblem(value) {
4084
+ if (!isPlainValue(value)) return "capability must be an object";
4085
+ const can = value.can;
4086
+ if (can !== "read" && can !== "execute") return "capability.can must be read or execute";
4087
+ const allowedKeys = can === "execute" ? [
4088
+ "can",
4089
+ "scope",
4090
+ "operation"
4091
+ ] : ["can", "scope"];
4092
+ const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key)).sort();
4093
+ if (unknownKeys.length > 0) return `unknown capability key "${unknownKeys[0]}"`;
4094
+ if (value.scope !== void 0) {
4095
+ if (typeof value.scope !== "string" || value.scope.length === 0) return "capability.scope must be a non-empty string";
4096
+ }
4097
+ if (can === "execute" && value.operation !== void 0) {
4098
+ const operation = value.operation;
4099
+ if (!Array.isArray(operation)) return "capability.operation must be an array";
4100
+ if (operation.length > 100) return `capability.operation exceeds 100 entries`;
4101
+ for (const entry of operation) if (typeof entry !== "string" || entry.length === 0) return "capability.operation entries must be non-empty strings";
4102
+ }
4103
+ return null;
4104
+ }
4105
+ /** Returns the first v1-rule violation, or null. Pure, total, deterministic. */
4106
+ function grantProblem(value) {
4107
+ if (!isPlainValue(value)) return "grant must be an object";
4108
+ const unknownKeys = Object.keys(value).filter((key) => !GRANT_KEYS.has(key)).sort();
4109
+ if (unknownKeys.length > 0) return `unknown grant key "${unknownKeys[0]}"`;
4110
+ if (typeof value.id !== "string" || value.id.length === 0) return "id must be a non-empty string";
4111
+ if (typeof value.description !== "string") return "description must be a string";
4112
+ if (value.effect !== "allow" && value.effect !== "deny") return "effect must be allow or deny";
4113
+ const capabilityValue = value.capability;
4114
+ const capability = capabilityProblem(capabilityValue);
4115
+ if (capability !== null) return capability;
4116
+ const capabilityScope = capabilityValue.scope;
4117
+ const principal = principalProblem(value.principal, capabilityScope);
4118
+ if (principal !== null) return principal;
4119
+ if (value.where !== void 0) return conditionProblem(value.where, capabilityScope, 1, { nodes: 100 });
4120
+ return null;
4121
+ }
4122
+ /** V1 shape rules plus the group-document group-principal ban. */
4123
+ function assertValidGrant(grant, documentType) {
4124
+ const grantId = isPlainValue(grant) && typeof grant.id === "string" ? grant.id : "";
4125
+ const problem = grantProblem(grant);
4126
+ if (problem !== null) throw new InvalidGrantError(grantId, problem);
4127
+ if (documentType === "@powerhousedao/document-group" && "group" in grant.principal) throw new GroupPrincipalNotAllowedError(grantId);
4128
+ }
4129
+ /**
4130
+ * Validates an initial grant list: the count cap, every grant, and — on a
4131
+ * creator-less policy — that some grant keeps the auth scope administrable.
4132
+ */
4133
+ function assertValidInitialGrants(grants, documentType, creator) {
4134
+ if (grants.length > 100) throw new InvalidGrantError("", `policy exceeds 100 grants`);
4135
+ for (const grant of grants) assertValidGrant(grant, documentType);
4136
+ if (creator === void 0 && !administrationReachable(grants)) throw new AuthAdministrationMissingError();
4137
+ }
4138
+ /**
4139
+ * Validates a grant upsert: the grant itself, the count cap on append, and
4140
+ * administration retention. Retention is checked on an append as well as an
4141
+ * in-place replace, because a grant appended after the administration grant can
4142
+ * shadow it (evaluation is last-applicable-grant-wins) and so take
4143
+ * administration away without removing anything.
4144
+ */
4145
+ function assertValidGrantUpsert(grant, existing, documentType, creator) {
4146
+ assertValidGrant(grant, documentType);
4147
+ const exists = existing.some((g) => g.id === grant.id);
4148
+ if (!exists && existing.length >= 100) throw new InvalidGrantError(grant.id, `policy exceeds 100 grants`);
4149
+ assertAuthAdministrationRetained(creator, existing, exists ? existing.map((g) => g.id === grant.id ? grant : g) : [...existing, grant], grant.id);
4150
+ }
4151
+ /**
4152
+ * The request whose coverage keeps a policy administrable: a subject who may
4153
+ * SET_GRANT can upsert any grant, so every other repair stays reachable.
4154
+ */
4155
+ const AUTH_ADMINISTRATION_REQUEST = {
4156
+ verb: "execute",
4157
+ scope: "auth",
4158
+ operation: "SET_GRANT"
4159
+ };
4160
+ /**
4161
+ * Whether some subject can still administer the auth scope under this grant list.
4162
+ *
4163
+ * Asks the evaluator rather than pattern-matching a single grant: evaluation is
4164
+ * last-applicable-grant-wins, so an allow that some later deny shadows keeps
4165
+ * nothing reachable even though it is still present in the list. Only anyone and
4166
+ * address principals are candidates, because those are the ones v1 can match at
4167
+ * all; a `where` condition or a group or match principal never applies.
4168
+ */
4169
+ function administrationReachable(grants) {
4170
+ return grants.some((grant) => {
4171
+ const subject = administrationCandidate(grant);
4172
+ if (subject === void 0) return false;
4173
+ return evaluateGrantStack(grants, subject, AUTH_ADMINISTRATION_REQUEST).decision === "allow";
4174
+ });
4175
+ }
4176
+ /**
4177
+ * The subject to test this grant with, or undefined when the grant could never
4178
+ * carry administration for anyone.
4179
+ */
4180
+ function administrationCandidate(grant) {
4181
+ if (grant.effect !== "allow" || grant.where !== void 0 || !capabilityCovers(grant.capability, AUTH_ADMINISTRATION_REQUEST)) return;
4182
+ if ("address" in grant.principal) return {
4183
+ address: grant.principal.address,
4184
+ key: void 0
4185
+ };
4186
+ if ("anyone" in grant.principal) return {
4187
+ address: void 0,
4188
+ key: void 0
4189
+ };
4190
+ }
4191
+ /**
4192
+ * A creator-less policy must always retain a grant permitting execute on the
4193
+ * auth scope; on a signed document the creator carve-out keeps administration
4194
+ * reachable instead. Rejects a change that takes the last such grant away. A
4195
+ * policy already without one is left alone: the change is not what locks it.
4196
+ */
4197
+ function assertAuthAdministrationRetained(creator, previous, next, grantId) {
4198
+ if (creator !== void 0) return;
4199
+ if (administrationReachable(previous) && !administrationReachable(next)) throw new AuthAdministrationLockoutError(grantId);
4200
+ }
4201
+ function capabilityCovers(capability, request) {
4202
+ if (capability.can !== request.verb) return false;
4203
+ const scope = capability.scope;
4204
+ if (scope !== void 0 && scope !== "*" && scope !== request.scope) return false;
4205
+ if (capability.can === "execute") {
4206
+ if (capability.operation === void 0) return true;
4207
+ return request.operation !== void 0 && capability.operation.includes(request.operation);
4208
+ }
4209
+ return true;
4210
+ }
4211
+ function principalMatches(principal, subject) {
4212
+ if ("anyone" in principal) return true;
4213
+ if ("address" in principal) return subject.address !== void 0 && subject.address.toLowerCase() === principal.address.toLowerCase();
4214
+ return false;
4215
+ }
4216
+ /**
4217
+ * Evaluates a v1 grant stack: default deny, last applicable grant wins, and
4218
+ * reports which grant decided it. Group and match principals and `where`
4219
+ * conditions are not evaluated yet; a grant that uses any of them never applies.
4220
+ */
4221
+ function evaluateGrantStack(grants, subject, request) {
4222
+ let applicable;
4223
+ for (const grant of grants) {
4224
+ if (grant.where !== void 0) continue;
4225
+ if (capabilityCovers(grant.capability, request) && principalMatches(grant.principal, subject)) applicable = grant;
4226
+ }
4227
+ if (applicable === void 0) return {
4228
+ decision: "deny",
4229
+ refusal: "no-applicable-grant"
4230
+ };
4231
+ if (applicable.effect === "deny") return {
4232
+ decision: "deny",
4233
+ refusal: "denied-by-grant",
4234
+ grantId: applicable.id
4235
+ };
4236
+ return { decision: "allow" };
4237
+ }
3745
4238
  const documentModelFileExtension = "phdm";
3746
4239
  const documentModelInitialLocalState = {};
3747
4240
  const documentModelInitialGlobalState = {
@@ -4323,7 +4816,306 @@ const documentModelGlobalState = {
4323
4816
  };
4324
4817
  const HASH_ALGORITHM_SHA1 = "sha1";
4325
4818
  const HASH_ENCODING_BASE64 = "base64";
4326
- const documentModelDocumentType = "powerhouse/document-model";
4819
+ /**
4820
+ * Creates a default PHAuthState
4821
+ */
4822
+ function defaultAuthState() {
4823
+ return {
4824
+ version: 0,
4825
+ grants: []
4826
+ };
4827
+ }
4828
+ /**
4829
+ * Creates a default PHDocumentState
4830
+ */
4831
+ function defaultDocumentState() {
4832
+ return {
4833
+ version: 0,
4834
+ hash: {
4835
+ algorithm: HASH_ALGORITHM_SHA1,
4836
+ encoding: HASH_ENCODING_BASE64
4837
+ }
4838
+ };
4839
+ }
4840
+ /**
4841
+ * Creates a default PHBaseState with auth and document properties
4842
+ */
4843
+ function defaultBaseState() {
4844
+ return {
4845
+ auth: defaultAuthState(),
4846
+ document: defaultDocumentState()
4847
+ };
4848
+ }
4849
+ /**
4850
+ * Creates a PHAuthState with the given properties
4851
+ */
4852
+ function createAuthState(auth) {
4853
+ return {
4854
+ ...defaultAuthState(),
4855
+ ...auth
4856
+ };
4857
+ }
4858
+ /**
4859
+ * Creates a PHDocumentState with the given properties
4860
+ */
4861
+ function createDocumentState(document) {
4862
+ return {
4863
+ ...defaultDocumentState(),
4864
+ ...document
4865
+ };
4866
+ }
4867
+ /**
4868
+ * Creates a PHBaseState with the given auth and document properties
4869
+ */
4870
+ function createBaseState(auth, document) {
4871
+ return {
4872
+ auth: createAuthState(auth),
4873
+ document: createDocumentState(document)
4874
+ };
4875
+ }
4876
+ /**
4877
+ * Backfills the auth scope to the default for legacy documents serialized with
4878
+ * an empty `auth`. Replaces only `state.auth`. Idempotent.
4879
+ */
4880
+ function backfillAuthState(state) {
4881
+ return {
4882
+ ...state,
4883
+ auth: createAuthState(state.auth)
4884
+ };
4885
+ }
4886
+ function defaultGlobalState() {
4887
+ return {
4888
+ ...defaultBaseState(),
4889
+ author: {
4890
+ name: "",
4891
+ website: ""
4892
+ },
4893
+ description: "",
4894
+ extension: "",
4895
+ id: "",
4896
+ name: "",
4897
+ specifications: []
4898
+ };
4899
+ }
4900
+ function defaultLocalState() {
4901
+ return {};
4902
+ }
4903
+ function createGlobalState(state) {
4904
+ return {
4905
+ ...defaultGlobalState(),
4906
+ ...state || {}
4907
+ };
4908
+ }
4909
+ function createLocalState(state) {
4910
+ return {
4911
+ ...defaultLocalState(),
4912
+ ...state || {}
4913
+ };
4914
+ }
4915
+ function createState(baseState, globalState, localState) {
4916
+ return {
4917
+ ...createBaseState(baseState?.auth, baseState?.document),
4918
+ global: createGlobalState(globalState),
4919
+ local: createLocalState(localState)
4920
+ };
4921
+ }
4922
+ /**
4923
+ * Destructuring a null input (reachable via raw synced operations) would
4924
+ * store an engine-specific TypeError message on the error operation.
4925
+ */
4926
+ function assertActionInputShape(input) {
4927
+ if (!isPlainValue(input)) throw new InvalidActionInputError({ input: "must be an object" });
4928
+ }
4929
+ function withGrants(document, grants) {
4930
+ return {
4931
+ ...document,
4932
+ state: {
4933
+ ...document.state,
4934
+ auth: {
4935
+ ...document.state.auth,
4936
+ grants
4937
+ }
4938
+ }
4939
+ };
4940
+ }
4941
+ const P256_PUBKEY_MULTICODEC = [128, 36];
4942
+ const DID_KEY_PREFIX = "did:key:z";
4943
+ function bytesEqual(a, b) {
4944
+ if (a.length !== b.length) return false;
4945
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
4946
+ return true;
4947
+ }
4948
+ /**
4949
+ * True when `signerKey` (an ActionSigner app key, a did:key) identifies the same
4950
+ * key recorded as the document creator. Returns false
4951
+ * when there is no creator (empty JWK) or no signer key.
4952
+ */
4953
+ function isDocumentCreator(creatorKey, signerKey) {
4954
+ if (!creatorKey?.x || !creatorKey.y) return false;
4955
+ if (!signerKey || !signerKey.startsWith(DID_KEY_PREFIX)) return false;
4956
+ const decoded = base58Decode(signerKey.slice(9));
4957
+ if (!decoded || decoded.length !== 35) return false;
4958
+ if (decoded[0] !== P256_PUBKEY_MULTICODEC[0] || decoded[1] !== P256_PUBKEY_MULTICODEC[1]) return false;
4959
+ const parityPrefix = decoded[2];
4960
+ if (parityPrefix !== 2 && parityPrefix !== 3) return false;
4961
+ const didX = decoded.subarray(3, 35);
4962
+ const jwkX = base64UrlToBytes(creatorKey.x);
4963
+ const jwkY = base64UrlToBytes(creatorKey.y);
4964
+ if (jwkX.length !== 32 || jwkY.length !== 32) return false;
4965
+ if (!bytesEqual(didX, jwkX)) return false;
4966
+ return (jwkY[31] & 1) === 1 === (parityPrefix === 3);
4967
+ }
4968
+ /**
4969
+ * Sets the initial policy. Valid only while the auth scope is uninitialized
4970
+ * (version 0). The input version is the policy language version and must be an
4971
+ * integer >= 1; 0 is reserved for the uninitialized state. On a signed-header
4972
+ * document it must be signed by the document creator (`header.sig.publicKey`).
4973
+ * A creator-less policy must include a grant permitting execute on the auth
4974
+ * scope, or it would be born locked out.
4975
+ */
4976
+ function applyInitializeAuthAction(document, action) {
4977
+ assertActionInputShape(action.input);
4978
+ const { version, grants } = action.input;
4979
+ if (!Number.isInteger(version) || version < 1) throw new InvalidAuthVersionError(document.header.id, version);
4980
+ if (document.state.auth.version !== 0) throw new AuthAlreadyInitializedError(document.header.id);
4981
+ if (!Array.isArray(grants)) throw new InvalidActionInputError({ grants: "must be an array" });
4982
+ const creatorKey = document.header.sig.publicKey;
4983
+ const signerKey = action.context?.signer?.app.key;
4984
+ const hasCreator = Boolean(creatorKey.kty || creatorKey.x || creatorKey.y);
4985
+ if (hasCreator && !isDocumentCreator(creatorKey, signerKey)) throw new AuthInitializerNotCreatorError(document.header.id);
4986
+ const creator = hasCreator ? signerKey : void 0;
4987
+ assertValidInitialGrants(grants, document.header.documentType, creator);
4988
+ return {
4989
+ ...document,
4990
+ state: {
4991
+ ...document.state,
4992
+ auth: createAuthState(creator ? {
4993
+ version,
4994
+ grants,
4995
+ creator
4996
+ } : {
4997
+ version,
4998
+ grants
4999
+ })
5000
+ }
5001
+ };
5002
+ }
5003
+ /** Upserts a grant by id: replaces in place if present, otherwise appends. */
5004
+ function applySetGrantAction(document, action) {
5005
+ assertActionInputShape(action.input);
5006
+ const { grant } = action.input;
5007
+ const grants = document.state.auth.grants;
5008
+ assertValidGrantUpsert(grant, grants, document.header.documentType, document.state.auth.creator);
5009
+ return withGrants(document, grants.some((g) => g.id === grant.id) ? grants.map((g) => g.id === grant.id ? grant : g) : [...grants, grant]);
5010
+ }
5011
+ /**
5012
+ * Removes a grant by id; throws if the id is not present or if the removal
5013
+ * would leave a creator-less policy with no auth-administration grant.
5014
+ */
5015
+ function applyRemoveGrantAction(document, action) {
5016
+ assertActionInputShape(action.input);
5017
+ const { id } = action.input;
5018
+ const { grants, creator } = document.state.auth;
5019
+ if (!grants.some((g) => g.id === id)) throw new GrantNotFoundError(id);
5020
+ const next = grants.filter((g) => g.id !== id);
5021
+ assertAuthAdministrationRetained(creator, grants, next, id);
5022
+ return withGrants(document, next);
5023
+ }
5024
+ /**
5025
+ * Moves a grant by id to a new index. Order is load-bearing (the last
5026
+ * applicable grant wins), so the relative order of the other grants is kept.
5027
+ * The target index is clamped to the valid range; an unknown id throws.
5028
+ *
5029
+ * Order alone decides which grant wins, so a move can take administration away
5030
+ * without changing the list's contents. It carries the same retention rule as
5031
+ * the two mutation paths.
5032
+ */
5033
+ function applyMoveGrantAction(document, action) {
5034
+ assertActionInputShape(action.input);
5035
+ const { id, index } = action.input;
5036
+ const { grants, creator } = document.state.auth;
5037
+ const from = grants.findIndex((g) => g.id === id);
5038
+ if (from === -1) throw new GrantNotFoundError(id);
5039
+ const next = [...grants];
5040
+ const [moved] = next.splice(from, 1);
5041
+ const to = Math.max(0, Math.min(index, next.length));
5042
+ next.splice(to, 0, moved);
5043
+ assertAuthAdministrationRetained(creator, grants, next, id);
5044
+ return withGrants(document, next);
5045
+ }
5046
+ /**
5047
+ * Dispatches an auth-scope action to its handler. This is the auth scope's
5048
+ * dedicated reducer: it is applied by the base reducer instead of the model
5049
+ * reducer, mirroring the document-scope platform handlers. Unknown types are a
5050
+ * no-op, matching the model-reducer default.
5051
+ */
5052
+ function applyAuthAction(document, action) {
5053
+ switch (action.type) {
5054
+ case "INITIALIZE_AUTH": return applyInitializeAuthAction(document, action);
5055
+ case "SET_GRANT": return applySetGrantAction(document, action);
5056
+ case "REMOVE_GRANT": return applyRemoveGrantAction(document, action);
5057
+ case "MOVE_GRANT": return applyMoveGrantAction(document, action);
5058
+ default: return document;
5059
+ }
5060
+ }
5061
+ /**
5062
+ * Because only creators can initialize auth scopes, we must verify that either
5063
+ * the document has no auth or the version and creator match.
5064
+ */
5065
+ function assertAuthPreservedOnDuplicate(documentId, source, duplicated) {
5066
+ if (!source || source.version === 0) return;
5067
+ if (duplicated === void 0 || duplicated.version !== source.version || duplicated.creator !== source.creator) throw new AuthPolicyNotPreservedError(documentId);
5068
+ }
5069
+ /**
5070
+ * The auth scope a state snapshot may install, given the policy already there.
5071
+ *
5072
+ * `applyAuthAction` is the validated door onto `state.auth`, but a whole-state
5073
+ * snapshot (UPGRADE_DOCUMENT's `initialState`, LOAD_STATE's `data`) replaces the
5074
+ * scope wholesale and is authorized as a `document`-scope write. Without this,
5075
+ * a subject holding `execute` on `document` and no auth grant at all can install
5076
+ * a policy of its choosing, name itself `creator` (which exempts the policy from
5077
+ * the retention rule for good), or wipe an existing policy by carrying the
5078
+ * default uninitialized one.
5079
+ *
5080
+ * Three cases:
5081
+ *
5082
+ * - the snapshot carries no policy, or an uninitialized one: the document
5083
+ * keeps the policy it has, so a default-state upgrade cannot reset it;
5084
+ * - the document is uninitialized and the snapshot carries a policy: the
5085
+ * policy is installed after the same validation genesis applies, so it
5086
+ * cannot be born locked out;
5087
+ * - both carry a policy: they must agree. A duplicate preserves its source's
5088
+ * policy (see {@link assertAuthPreservedOnDuplicate}), and anything else is
5089
+ * an attempt to replace one policy with another.
5090
+ */
5091
+ function resolveSnapshotAuth(documentId, documentType, current, incoming) {
5092
+ const currentAuth = current ?? createAuthState({
5093
+ version: 0,
5094
+ grants: []
5095
+ });
5096
+ if (!incoming || !incoming.version) return currentAuth;
5097
+ if (currentAuth.version !== 0) {
5098
+ if (stringify$1(incoming) !== stringify$1(currentAuth)) throw new AuthPolicyNotPreservedError(documentId);
5099
+ return incoming;
5100
+ }
5101
+ if (!Array.isArray(incoming.grants)) throw new InvalidActionInputError({ grants: "must be an array" });
5102
+ assertValidInitialGrants(incoming.grants, documentType, incoming.creator);
5103
+ return incoming;
5104
+ }
5105
+ /** UNDO, REDO and PRUNE are rejected on the auth scope. */
5106
+ function assertAuthScopeActionAllowed(action) {
5107
+ if (action.scope === "auth" && [
5108
+ "UNDO",
5109
+ "REDO",
5110
+ "PRUNE"
5111
+ ].includes(action.type)) throw new AuthActionNotAllowedError(action.type);
5112
+ }
5113
+ /**
5114
+ * True iff authorization rejected the action.
5115
+ */
5116
+ function isDenied(operation) {
5117
+ return operation.deniedReason !== void 0;
5118
+ }
4327
5119
  const BaseDocumentHeaderSchema = object({
4328
5120
  id: string(),
4329
5121
  name: string(),
@@ -4454,6 +5246,7 @@ function createDocumentScopeOperations(header, state) {
4454
5246
  function baseCreateDocument(createState, initialState, documentType = "") {
4455
5247
  const state = createState(initialState);
4456
5248
  const header = createPresignedHeader(generateId(), documentType);
5249
+ header.protocolVersions = { "base-reducer": 2 };
4457
5250
  return {
4458
5251
  header,
4459
5252
  state,
@@ -4477,6 +5270,18 @@ function hashDocumentStateForScope(document, scope = "global") {
4477
5270
  * Uses the V2 model where consecutive NOOPs form chains.
4478
5271
  * Unlike V1 garbageCollect, this preserves ALL operations but marks which to apply.
4479
5272
  */
5273
+ /**
5274
+ * The base-reducer protocol version a document is written against. Every
5275
+ * document carries one, set from the CREATE_DOCUMENT input. A header without
5276
+ * one was built outside that path, and choosing a version on its behalf would
5277
+ * replay the document through a reducer it was not written for, so it is an
5278
+ * error rather than a default.
5279
+ */
5280
+ function baseReducerVersion(header) {
5281
+ const version = header.protocolVersions?.["base-reducer"];
5282
+ if (typeof version !== "number") throw new Error(`Document ${header.id} carries no base-reducer protocol version`);
5283
+ return version;
5284
+ }
4480
5285
  function garbageCollectV2(sortedOperations) {
4481
5286
  const result = [];
4482
5287
  let noopChainLength = 0;
@@ -4493,9 +5298,26 @@ function garbageCollectV2(sortedOperations) {
4493
5298
  const defaultCreateState = (state) => {
4494
5299
  return state;
4495
5300
  };
5301
+ /**
5302
+ * Records an operation in the history without applying it, which is what a
5303
+ * denied operation needs: it occupies its index and contributes no state.
5304
+ *
5305
+ * The scope defaults to the action's own, and is passed explicitly by a rebuild
5306
+ * that is walking one stream and does not want to trust the action's copy.
5307
+ */
5308
+ function appendWithoutApplying(document, operation, scope = operation.action.scope) {
5309
+ return {
5310
+ ...document,
5311
+ operations: {
5312
+ ...document.operations,
5313
+ [scope]: [...document.operations[scope] ?? [], operation]
5314
+ }
5315
+ };
5316
+ }
4496
5317
  function replayDocument(initialState, operations, reducer, header, dispatch, skipHeaderOperations = {}, options) {
4497
5318
  const { checkHashes = true, reuseOperationResultingState, operationResultingStateParser = parseResultingState, skipIndexValidation } = options || {};
4498
- let documentState = initialState;
5319
+ const backfilledInitialState = backfillAuthState(initialState);
5320
+ let documentState = backfilledInitialState;
4499
5321
  const operationsToReplay = [];
4500
5322
  const allScopes = new Set([
4501
5323
  ...Object.keys(operations),
@@ -4530,12 +5352,13 @@ function replayDocument(initialState, operations, reducer, header, dispatch, ski
4530
5352
  const document = {
4531
5353
  header,
4532
5354
  state: defaultCreateState(documentState),
4533
- initialState,
5355
+ initialState: backfilledInitialState,
4534
5356
  operations: initialOperations,
4535
5357
  clipboard: []
4536
5358
  };
4537
5359
  let result = document;
4538
5360
  if (operationsToReplay.length) result = operationsToReplay.reduce((document, operation) => {
5361
+ if (isDenied(operation)) return appendWithoutApplying(document, operation);
4539
5362
  return reducer(document, operation.action, dispatch, {
4540
5363
  ignoreSkipOperations: true,
4541
5364
  checkHashes,
@@ -4817,13 +5640,15 @@ function redoOperation(document, action, skip) {
4817
5640
  });
4818
5641
  }
4819
5642
  function loadStateOperation(document, action) {
5643
+ const loaded = backfillAuthState(action.state.data);
5644
+ loaded.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, loaded.auth);
4820
5645
  return {
4821
5646
  ...document,
4822
5647
  header: {
4823
5648
  ...document.header,
4824
5649
  name: action.state.name
4825
5650
  },
4826
- state: action.state.data
5651
+ state: loaded
4827
5652
  };
4828
5653
  }
4829
5654
  function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
@@ -4997,12 +5822,13 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
4997
5822
  function baseReducer(document, action, customReducer, dispatch, options = {}) {
4998
5823
  const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main" } = options;
4999
5824
  let _action = actionFromAction(action);
5825
+ assertAuthScopeActionAllowed(_action);
5000
5826
  let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
5001
5827
  let newDocument = { ...document };
5002
5828
  let reuseLastOperationIndex = false;
5003
5829
  const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
5004
5830
  if (isUndoRedo(_action)) {
5005
- const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? 1);
5831
+ const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? baseReducerVersion(document.header));
5006
5832
  _action = transformedAction;
5007
5833
  skipValue = calculatedSkip;
5008
5834
  newDocument = processedDocument;
@@ -5018,7 +5844,7 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5018
5844
  branch
5019
5845
  };
5020
5846
  newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
5021
- const protocolVersion = options.protocolVersion ?? 1;
5847
+ const protocolVersion = options.protocolVersion ?? baseReducerVersion(document.header);
5022
5848
  if (isUndo(action) && protocolVersion < 2) return processUndoOperation(newDocument, action.scope, customReducer);
5023
5849
  const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
5024
5850
  if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
@@ -5044,6 +5870,13 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5044
5870
  }
5045
5871
  newDocument = create(newDocument, (draft) => {
5046
5872
  try {
5873
+ if (_action.scope === "auth") {
5874
+ const authState = applyAuthAction(newDocument, _action).state;
5875
+ unsafe(() => {
5876
+ draft.state = castDraft(authState);
5877
+ });
5878
+ return;
5879
+ }
5047
5880
  const newState = customReducer(draft.state, _action, dispatch);
5048
5881
  if (newState) unsafe(() => {
5049
5882
  draft.state = castDraft(newState);
@@ -5837,10 +6670,12 @@ function applyInitialState(document, action) {
5837
6670
  const input = action.input;
5838
6671
  const newState = input.initialState || input.state;
5839
6672
  if (newState) {
5840
- document.state = {
6673
+ const merged = backfillAuthState({
5841
6674
  ...document.state,
5842
6675
  ...newState
5843
- };
6676
+ });
6677
+ merged.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, merged.auth);
6678
+ document.state = merged;
5844
6679
  document.initialState = document.state;
5845
6680
  }
5846
6681
  }
@@ -5900,7 +6735,6 @@ function computeUpgradeTransitions(manifest, fromVersion, toVersion) {
5900
6735
  }
5901
6736
  return transitions;
5902
6737
  }
5903
- const NON_DOMAIN_SCOPES$1 = new Set(["auth", "document"]);
5904
6738
  function highestReducerVersion(reducers) {
5905
6739
  const keys = Object.keys(reducers).map(Number);
5906
6740
  if (keys.length === 0) throw new Error("VersionedReplayConfig.reducers must not be empty");
@@ -5928,15 +6762,15 @@ function highestReducerVersion(reducers) {
5928
6762
  */
5929
6763
  function replayDocumentVersioned(initialState, operations, config, header, dispatch, options) {
5930
6764
  const { checkHashes = true, skipIndexValidation } = options || {};
5931
- const protocolVersion = header.protocolVersions?.["base-reducer"] ?? 1;
6765
+ const protocolVersion = baseReducerVersion(header);
5932
6766
  const spine = (operations["document"] ?? []).slice().sort((a, b) => a.index - b.index);
5933
6767
  const upgrades = spine.filter((op) => op.action.type === "UPGRADE_DOCUMENT");
5934
6768
  const legacyFallback = () => {
5935
- const domainOps = Object.fromEntries(Object.entries(operations).filter(([s]) => !NON_DOMAIN_SCOPES$1.has(s)));
6769
+ const replayOps = Object.fromEntries(Object.entries(operations).filter(([s]) => s !== "document"));
5936
6770
  const latestVersion = highestReducerVersion(config.reducers);
5937
6771
  const reducer = config.reducers[latestVersion];
5938
6772
  return {
5939
- ...replayDocument(initialState, domainOps, reducer, header, dispatch, {}, options),
6773
+ ...replayDocument(initialState, replayOps, reducer, header, dispatch, {}, options),
5940
6774
  operations
5941
6775
  };
5942
6776
  };
@@ -5960,14 +6794,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5960
6794
  const a = op.action;
5961
6795
  return a.input.fromVersion > 0 && a.input.fromVersion < a.input.toVersion;
5962
6796
  });
5963
- const domainScopes = Object.keys(operations).filter((s) => !NON_DOMAIN_SCOPES$1.has(s));
6797
+ const replayScopes = Object.keys(operations).filter((s) => s !== "document");
5964
6798
  const scopeOps = {};
5965
- for (const s of domainScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
6799
+ for (const s of replayScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
5966
6800
  const boundaries = validatedUpgrades.map((upgradeOp) => {
5967
6801
  const revisionSnapshot = upgradeOp.action.input.revision;
5968
6802
  const upgradeTimestamp = upgradeOp.timestampUtcMs;
5969
6803
  const boundary = {};
5970
- for (const s of domainScopes) {
6804
+ for (const s of replayScopes) {
5971
6805
  const ops = scopeOps[s] ?? [];
5972
6806
  if (revisionSnapshot !== void 0) {
5973
6807
  const rev = revisionSnapshot[s] ?? 0;
@@ -5985,7 +6819,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5985
6819
  }
5986
6820
  return boundary;
5987
6821
  });
5988
- for (let i = 1; i < boundaries.length; i++) for (const s of domainScopes) {
6822
+ for (let i = 1; i < boundaries.length; i++) for (const s of replayScopes) {
5989
6823
  const prev = boundaries[i - 1]?.[s] ?? 0;
5990
6824
  const curr = boundaries[i]?.[s] ?? 0;
5991
6825
  if (boundaries[i]) boundaries[i][s] = Math.max(prev, curr);
@@ -5997,10 +6831,11 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5997
6831
  ]);
5998
6832
  const initialOperations = {};
5999
6833
  for (const s of allScopes) initialOperations[s] = [];
6834
+ const backfilledSeed = backfillAuthState(seedState);
6000
6835
  let document = {
6001
6836
  header,
6002
- state: seedState,
6003
- initialState: seedState,
6837
+ state: backfilledSeed,
6838
+ initialState: backfilledSeed,
6004
6839
  operations: initialOperations,
6005
6840
  clipboard: []
6006
6841
  };
@@ -6012,13 +6847,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6012
6847
  const available = Object.keys(config.reducers).join(", ");
6013
6848
  throw new Error(`No reducer registered for document version ${currentVersion}. Available versions: ${available}`);
6014
6849
  }
6015
- for (const s of domainScopes) {
6850
+ for (const s of replayScopes) {
6016
6851
  const ops = scopeOps[s] ?? [];
6017
6852
  const segStart = k === 0 ? 0 : boundaries[k - 1]?.[s] ?? 0;
6018
6853
  const segEnd = k < validatedUpgrades.length ? boundaries[k]?.[s] ?? ops.length : ops.length;
6019
6854
  const segOps = ops.slice(segStart, segEnd);
6020
6855
  for (const op of segOps) {
6021
- document = reducer(document, op.action, dispatch, {
6856
+ if (isDenied(op)) document = appendWithoutApplying(document, op, s);
6857
+ else document = reducer(document, op.action, dispatch, {
6022
6858
  ignoreSkipOperations: true,
6023
6859
  checkHashes,
6024
6860
  skipIndexValidation,
@@ -6035,6 +6871,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6035
6871
  if (!spineOp) continue;
6036
6872
  const spineActionType = spineOp.action.type;
6037
6873
  if (spineActionType === "CREATE_DOCUMENT" || spineActionType === "UPGRADE_DOCUMENT") continue;
6874
+ if (isDenied(spineOp)) continue;
6038
6875
  if (spineActionType === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, spineOp.action);
6039
6876
  else document = reducer(document, spineOp.action, dispatch, {
6040
6877
  ignoreSkipOperations: true,
@@ -6065,7 +6902,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6065
6902
  }
6066
6903
  };
6067
6904
  if (!checkHashes) {
6068
- const allReplayedOps = domainScopes.flatMap((s) => scopeOps[s] ?? []);
6905
+ const allReplayedOps = replayScopes.flatMap((s) => scopeOps[s] ?? []);
6069
6906
  for (const scope of Object.keys(document.state)) {
6070
6907
  const capturedHash = segmentEndHashPerScope.get(scope);
6071
6908
  const scopeHash = capturedHash !== void 0 ? capturedHash : hashDocumentStateForScope(document, scope);
@@ -6107,7 +6944,6 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6107
6944
  }
6108
6945
  };
6109
6946
  }
6110
- const NON_DOMAIN_SCOPES = new Set(["auth", "document"]);
6111
6947
  function zipAsync(data) {
6112
6948
  return new Promise((resolve, reject) => {
6113
6949
  zip(data, (err, out) => err ? reject(err) : resolve(out));
@@ -6186,7 +7022,7 @@ async function parseZipData(data) {
6186
7022
  async function loadFromZipData(data, reducer, options) {
6187
7023
  const { initialState, header, clearedOperations } = await parseZipData(data);
6188
7024
  return {
6189
- ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => !NON_DOMAIN_SCOPES.has(scope))), reducer, header, void 0, {}, options),
7025
+ ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => scope !== "document")), reducer, header, void 0, {}, options),
6190
7026
  operations: clearedOperations
6191
7027
  };
6192
7028
  }
@@ -6209,96 +7045,6 @@ const documentModelLoadFromInput = (input) => {
6209
7045
  const documentModelSaveToFileHandle = (document, input) => {
6210
7046
  return baseSaveToFileHandle(document, input);
6211
7047
  };
6212
- /**
6213
- * Creates a default PHAuthState
6214
- */
6215
- function defaultAuthState() {
6216
- return {};
6217
- }
6218
- /**
6219
- * Creates a default PHDocumentState
6220
- */
6221
- function defaultDocumentState() {
6222
- return {
6223
- version: 0,
6224
- hash: {
6225
- algorithm: HASH_ALGORITHM_SHA1,
6226
- encoding: HASH_ENCODING_BASE64
6227
- }
6228
- };
6229
- }
6230
- /**
6231
- * Creates a default PHBaseState with auth and document properties
6232
- */
6233
- function defaultBaseState() {
6234
- return {
6235
- auth: defaultAuthState(),
6236
- document: defaultDocumentState()
6237
- };
6238
- }
6239
- /**
6240
- * Creates a PHAuthState with the given properties
6241
- */
6242
- function createAuthState(auth) {
6243
- return {
6244
- ...defaultAuthState(),
6245
- ...auth
6246
- };
6247
- }
6248
- /**
6249
- * Creates a PHDocumentState with the given properties
6250
- */
6251
- function createDocumentState(document) {
6252
- return {
6253
- ...defaultDocumentState(),
6254
- ...document
6255
- };
6256
- }
6257
- /**
6258
- * Creates a PHBaseState with the given auth and document properties
6259
- */
6260
- function createBaseState(auth, document) {
6261
- return {
6262
- auth: createAuthState(auth),
6263
- document: createDocumentState(document)
6264
- };
6265
- }
6266
- function defaultGlobalState() {
6267
- return {
6268
- ...defaultBaseState(),
6269
- author: {
6270
- name: "",
6271
- website: ""
6272
- },
6273
- description: "",
6274
- extension: "",
6275
- id: "",
6276
- name: "",
6277
- specifications: []
6278
- };
6279
- }
6280
- function defaultLocalState() {
6281
- return {};
6282
- }
6283
- function createGlobalState(state) {
6284
- return {
6285
- ...defaultGlobalState(),
6286
- ...state || {}
6287
- };
6288
- }
6289
- function createLocalState(state) {
6290
- return {
6291
- ...defaultLocalState(),
6292
- ...state || {}
6293
- };
6294
- }
6295
- function createState(baseState, globalState, localState) {
6296
- return {
6297
- ...createBaseState(baseState?.auth, baseState?.document),
6298
- global: createGlobalState(globalState),
6299
- local: createLocalState(localState)
6300
- };
6301
- }
6302
7048
  //#endregion
6303
7049
  //#region ../document-model/dist/index.js
6304
7050
  const isStateOfType$1 = (state) => {
@@ -7377,6 +8123,6 @@ const driveDocumentModelModule = {
7377
8123
  }
7378
8124
  };
7379
8125
  //#endregion
7380
- 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 };
8126
+ 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 };
7381
8127
 
7382
- //# sourceMappingURL=document-drive-DjL9iikf.mjs.map
8128
+ //# sourceMappingURL=document-drive-CI0J7nMj.mjs.map