@powerhousedao/powerhouse-vetra-packages 6.2.2-dev.6 → 6.2.2-dev.61

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 (103) hide show
  1. package/dist/browser/assets/{entry-Cvoo4_c9.js → entry-D-JZwwGk.js} +3 -2
  2. package/dist/browser/assets/{projection-entry-DVec8YRh.js → projection-entry-64-WZZjU.js} +4 -4
  3. package/dist/browser/{linting-Dn6pXRkK.js → button-CuO8Gh3v.js} +20 -6
  4. package/dist/browser/button-CuO8Gh3v.js.map +1 -0
  5. package/dist/browser/{connect-KEU-HR3o.js → connect-D1KIMa_5.js} +916 -200
  6. package/dist/browser/connect-D1KIMa_5.js.map +1 -0
  7. package/dist/browser/{dist-CXoKspdx.js → dist-B4FWbuEM.js} +10 -2
  8. package/dist/browser/{dist-CXoKspdx.js.map → dist-B4FWbuEM.js.map} +1 -1
  9. package/dist/{node/dist-BeZaEP6S.mjs → browser/dist-BAJ5nRDl.js} +21 -2
  10. package/dist/browser/{dist-D8H48e8N.js.map → dist-BAJ5nRDl.js.map} +1 -1
  11. package/dist/browser/{dist-D5ra2aRj.js → dist-dmLcxsWV.js} +1588 -1293
  12. package/dist/browser/dist-dmLcxsWV.js.map +1 -0
  13. package/dist/browser/{document-drive-IdFV1QXL.js → document-drive-cxHraTVQ.js} +1157 -122
  14. package/dist/browser/document-drive-cxHraTVQ.js.map +1 -0
  15. package/dist/browser/document-models/index.js +1 -1
  16. package/dist/browser/{documents-D4ek7Ubv.js → documents-J_l59mSV.js} +5 -604
  17. package/dist/browser/documents-J_l59mSV.js.map +1 -0
  18. package/dist/browser/{editor-DiZF3ao3.js → editor-BAUTcFP2.js} +671 -236
  19. package/dist/browser/editor-BAUTcFP2.js.map +1 -0
  20. package/dist/browser/editor-PmQcB82h.js +25 -0
  21. package/dist/browser/editor-PmQcB82h.js.map +1 -0
  22. package/dist/browser/editors/document-model-editor/module.js +1 -1
  23. package/dist/browser/editors/generic-drive-explorer/index.js +5 -5
  24. package/dist/browser/editors/generic-drive-explorer/module.js +1 -1
  25. package/dist/{node/folder-view-Cy-QxsDg.mjs → browser/folder-view-CWnDdLMC.js} +73 -46
  26. package/dist/browser/folder-view-CWnDdLMC.js.map +1 -0
  27. package/dist/browser/{graphql-editor-ZLRO_8Ce.js → graphql-editor-Cq_L5IUQ.js} +8 -7
  28. package/dist/browser/{graphql-editor-ZLRO_8Ce.js.map → graphql-editor-Cq_L5IUQ.js.map} +1 -1
  29. package/dist/browser/{hooks-CORHBNHb.js → hooks-KKcNp2X0.js} +2 -2
  30. package/dist/browser/{hooks-CORHBNHb.js.map → hooks-KKcNp2X0.js.map} +1 -1
  31. package/dist/browser/index.js +378 -9
  32. package/dist/browser/index.js.map +1 -1
  33. package/dist/browser/{json-editor-DBuZ6gN7.js → json-editor-D8lysNHE.js} +4 -4
  34. package/dist/browser/{json-editor-DBuZ6gN7.js.map → json-editor-D8lysNHE.js.map} +1 -1
  35. package/dist/browser/{lib-C-f3izXz.js → lib-DKvC6Hnv.js} +13 -4
  36. package/dist/browser/lib-DKvC6Hnv.js.map +1 -0
  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-BjQKM498.js → state-schemas-NyThZN-C.js} +8 -18
  42. package/dist/browser/state-schemas-NyThZN-C.js.map +1 -0
  43. package/dist/node/{linting-Dx50GuLN.mjs → button-Da7CxMn7.mjs} +20 -7
  44. package/dist/node/button-Da7CxMn7.mjs.map +1 -0
  45. package/dist/node/{connect-KcYZcy_Z.mjs → connect-BixjoUPL.mjs} +916 -200
  46. package/dist/node/connect-BixjoUPL.mjs.map +1 -0
  47. package/dist/node/{dist-DJEK2WVi.mjs → dist-BtnA_6jV.mjs} +1592 -1292
  48. package/dist/node/dist-BtnA_6jV.mjs.map +1 -0
  49. package/dist/{browser/dist-D8H48e8N.js → node/dist-CzYOpeXN.mjs} +21 -2
  50. package/dist/node/{dist-BeZaEP6S.mjs.map → dist-CzYOpeXN.mjs.map} +1 -1
  51. package/dist/node/{dist-fA4EItr0.mjs → dist-DT7jI9ol.mjs} +10 -2
  52. package/dist/node/{dist-fA4EItr0.mjs.map → dist-DT7jI9ol.mjs.map} +1 -1
  53. package/dist/node/{document-drive-C8hbqQ3C.mjs → document-drive-ChLoGFvU.mjs} +1157 -122
  54. package/dist/node/document-drive-ChLoGFvU.mjs.map +1 -0
  55. package/dist/node/document-models/index.mjs +1 -1
  56. package/dist/node/{documents-BFBPmyw1.mjs → documents-DCf53bzO.mjs} +3 -3
  57. package/dist/node/{documents-BFBPmyw1.mjs.map → documents-DCf53bzO.mjs.map} +1 -1
  58. package/dist/node/editor-C8Drq-be.mjs +25 -0
  59. package/dist/node/editor-C8Drq-be.mjs.map +1 -0
  60. package/dist/node/{editor-B1KhoF1f.mjs → editor-cfoi-9LF.mjs} +672 -237
  61. package/dist/node/editor-cfoi-9LF.mjs.map +1 -0
  62. package/dist/node/editors/document-model-editor/module.mjs +1 -1
  63. package/dist/node/editors/generic-drive-explorer/index.mjs +5 -5
  64. package/dist/node/editors/generic-drive-explorer/module.mjs +1 -1
  65. package/dist/{browser/folder-view-DMFqcJhc.js → node/folder-view--I9GOsir.mjs} +73 -46
  66. package/dist/node/folder-view--I9GOsir.mjs.map +1 -0
  67. package/dist/node/{graphql-DskHmkMe.mjs → graphql-BfAoPeWM.mjs} +2 -2
  68. package/dist/node/{graphql-DskHmkMe.mjs.map → graphql-BfAoPeWM.mjs.map} +1 -1
  69. package/dist/node/{graphql-editor-C5VLRqEf.mjs → graphql-editor-BKGQ8ojN.mjs} +7 -7
  70. package/dist/node/{graphql-editor-C5VLRqEf.mjs.map → graphql-editor-BKGQ8ojN.mjs.map} +1 -1
  71. package/dist/node/{hooks-DJrjfVaS.mjs → hooks-D0DcS3MV.mjs} +2 -2
  72. package/dist/node/{hooks-DJrjfVaS.mjs.map → hooks-D0DcS3MV.mjs.map} +1 -1
  73. package/dist/node/index.mjs +378 -9
  74. package/dist/node/index.mjs.map +1 -1
  75. package/dist/node/{json-editor-iI2s9a_4.mjs → json-editor-32paCJkU.mjs} +4 -4
  76. package/dist/node/{json-editor-iI2s9a_4.mjs.map → json-editor-32paCJkU.mjs.map} +1 -1
  77. package/dist/node/{main-D82dkD7g.mjs → main-DRfab-vL.mjs} +15 -6
  78. package/dist/node/main-DRfab-vL.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-Bl7nA8bB.mjs → state-schemas-ClrkM0Fn.mjs} +10 -20
  82. package/dist/{browser/state-schemas-BjQKM498.js.map → node/state-schemas-ClrkM0Fn.mjs.map} +1 -1
  83. package/package.json +8 -7
  84. package/dist/browser/connect-KEU-HR3o.js.map +0 -1
  85. package/dist/browser/dist-D5ra2aRj.js.map +0 -1
  86. package/dist/browser/document-drive-IdFV1QXL.js.map +0 -1
  87. package/dist/browser/documents-D4ek7Ubv.js.map +0 -1
  88. package/dist/browser/editor-DiZF3ao3.js.map +0 -1
  89. package/dist/browser/editor-SqNULDP-.js +0 -29
  90. package/dist/browser/editor-SqNULDP-.js.map +0 -1
  91. package/dist/browser/folder-view-DMFqcJhc.js.map +0 -1
  92. package/dist/browser/lib-C-f3izXz.js.map +0 -1
  93. package/dist/browser/linting-Dn6pXRkK.js.map +0 -1
  94. package/dist/node/connect-KcYZcy_Z.mjs.map +0 -1
  95. package/dist/node/dist-DJEK2WVi.mjs.map +0 -1
  96. package/dist/node/document-drive-C8hbqQ3C.mjs.map +0 -1
  97. package/dist/node/editor-B1KhoF1f.mjs.map +0 -1
  98. package/dist/node/editor-yBGEvMGD.mjs +0 -29
  99. package/dist/node/editor-yBGEvMGD.mjs.map +0 -1
  100. package/dist/node/folder-view-Cy-QxsDg.mjs.map +0 -1
  101. package/dist/node/linting-Dx50GuLN.mjs.map +0 -1
  102. package/dist/node/main-D82dkD7g.mjs.map +0 -1
  103. package/dist/node/state-schemas-Bl7nA8bB.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();
@@ -2946,6 +2980,76 @@ function castDraft(value) {
2946
2980
  }
2947
2981
  //#endregion
2948
2982
  //#region ../shared/dist/document-model/index.js
2983
+ /**
2984
+ * What separates a signature's params when it travels as one string.
2985
+ *
2986
+ * GraphQL declares `signatures` as a list of strings, not a list of lists, so a
2987
+ * tuple is joined for transport and split on arrival. The separator is here so
2988
+ * the two halves cannot disagree about it - they live in different packages, and
2989
+ * a mismatch would corrupt every signature that crossed the wire rather than
2990
+ * failing outright.
2991
+ */
2992
+ const SIGNATURE_PARAM_SEPARATOR = ", ";
2993
+ /** The number of params a signature carries. */
2994
+ const SIGNATURE_PARAM_COUNT = 5;
2995
+ /** Joins a signature's params for transport. Already-joined input passes through. */
2996
+ function serializeSignature(signature) {
2997
+ return Array.isArray(signature) ? signature.join(SIGNATURE_PARAM_SEPARATOR) : signature;
2998
+ }
2999
+ /**
3000
+ * Splits a transported signature back into its params. A tuple passes through.
3001
+ *
3002
+ * Short input is padded rather than refused: verification reads the params by
3003
+ * position and fails on a wrong one, which says more than a length complaint
3004
+ * raised here would.
3005
+ */
3006
+ function deserializeSignature(signature) {
3007
+ if (Array.isArray(signature)) return signature;
3008
+ const parts = signature.split(SIGNATURE_PARAM_SEPARATOR);
3009
+ return Array.from({ length: SIGNATURE_PARAM_COUNT }, (_unused, index) => parts[index] ?? "");
3010
+ }
3011
+ /**
3012
+ * Projects an action onto the fields the GraphQL `ActionInput` declares.
3013
+ *
3014
+ * A projection rather than a spread, because an input object rejects a field it
3015
+ * does not declare, and that refusal takes the whole request with it. An action
3016
+ * read back out of storage can carry fields the type no longer has - a legacy
3017
+ * `attachments` array, or the operation fields left behind by a signing helper
3018
+ * that returns an operation - and any one of them would sink an otherwise valid
3019
+ * submission.
3020
+ *
3021
+ * Only fields the action actually carries are emitted, so an unsigned action
3022
+ * sends no context at all rather than a context full of nulls.
3023
+ */
3024
+ function toTransportAction(action) {
3025
+ if (action.input === void 0 || action.input === null) throw new Error(`Action ${action.id} (${action.type}) has no input, which the wire requires`);
3026
+ const projected = {
3027
+ id: action.id,
3028
+ type: action.type,
3029
+ timestampUtcMs: action.timestampUtcMs,
3030
+ input: action.input,
3031
+ scope: action.scope
3032
+ };
3033
+ const context = toTransportContext(action.context);
3034
+ return context ? {
3035
+ ...projected,
3036
+ context
3037
+ } : projected;
3038
+ }
3039
+ function toTransportContext(context) {
3040
+ if (!context) return;
3041
+ const projected = {};
3042
+ if (context.prevOpIndex !== void 0) projected.prevOpIndex = context.prevOpIndex;
3043
+ if (context.prevOpHash !== void 0) projected.prevOpHash = context.prevOpHash;
3044
+ if (context.nonce !== void 0) projected.nonce = context.nonce;
3045
+ const signer = context.signer;
3046
+ if (signer) projected.signer = {
3047
+ ...signer.user ? { user: signer.user } : {},
3048
+ ...signer.app ? { app: signer.app } : {},
3049
+ signatures: (signer.signatures ?? []).map(serializeSignature)
3050
+ };
3051
+ return Object.keys(projected).length > 0 ? projected : void 0;
3052
+ }
2949
3053
  var InvalidActionInputError = class extends Error {
2950
3054
  data;
2951
3055
  constructor(data) {
@@ -2978,6 +3082,75 @@ var DowngradeNotSupportedError = class extends Error {
2978
3082
  this.toVersion = toVersion;
2979
3083
  }
2980
3084
  };
3085
+ /**
3086
+ * Thrown when INITIALIZE_AUTH is applied to an already-initialized auth scope.
3087
+ * The genesis action is valid only at auth revision zero.
3088
+ */
3089
+ var AuthAlreadyInitializedError = class extends Error {
3090
+ documentId;
3091
+ constructor(documentId) {
3092
+ super(`Auth scope already initialized for document ${documentId}: INITIALIZE_AUTH is valid only at auth revision zero`);
3093
+ this.name = "AuthAlreadyInitializedError";
3094
+ this.documentId = documentId;
3095
+ }
3096
+ };
3097
+ /**
3098
+ * Thrown when INITIALIZE_AUTH is not signed by the document creator (its signer
3099
+ * does not match `header.sig.publicKey`), so it cannot set the auth policy.
3100
+ */
3101
+ var AuthInitializerNotCreatorError = class extends Error {
3102
+ documentId;
3103
+ constructor(documentId) {
3104
+ super(`INITIALIZE_AUTH for document ${documentId} must be signed by the document creator`);
3105
+ this.name = "AuthInitializerNotCreatorError";
3106
+ this.documentId = documentId;
3107
+ }
3108
+ };
3109
+ /**
3110
+ * Thrown when INITIALIZE_AUTH carries a version below 1. Version 0 is reserved
3111
+ * for the uninitialized auth scope.
3112
+ */
3113
+ var InvalidAuthVersionError = class extends Error {
3114
+ documentId;
3115
+ version;
3116
+ constructor(documentId, version) {
3117
+ super(`Invalid auth policy version ${version} for document ${documentId}: INITIALIZE_AUTH requires an integer version >= 1`);
3118
+ this.name = "InvalidAuthVersionError";
3119
+ this.documentId = documentId;
3120
+ this.version = version;
3121
+ }
3122
+ };
3123
+ /** Thrown when a duplicate would lose the source policy's version or creator binding. */
3124
+ var AuthPolicyNotPreservedError = class extends Error {
3125
+ documentId;
3126
+ constructor(documentId) {
3127
+ super(`Duplicating document ${documentId} would not preserve its auth policy: the copy loses the policy version or its creator binding`);
3128
+ this.name = "AuthPolicyNotPreservedError";
3129
+ this.documentId = documentId;
3130
+ }
3131
+ };
3132
+ /**
3133
+ * Thrown when a grant referenced by REMOVE_GRANT or MOVE_GRANT does not exist.
3134
+ */
3135
+ var GrantNotFoundError = class extends Error {
3136
+ grantId;
3137
+ constructor(grantId) {
3138
+ super(`Grant not found in auth scope: ${grantId}`);
3139
+ this.name = "GrantNotFoundError";
3140
+ this.grantId = grantId;
3141
+ }
3142
+ };
3143
+ /**
3144
+ * Thrown when a disallowed action (UNDO, REDO, PRUNE) targets the auth scope.
3145
+ */
3146
+ var AuthActionNotAllowedError = class extends Error {
3147
+ actionType;
3148
+ constructor(actionType) {
3149
+ super(`${actionType} is not permitted on the auth scope`);
3150
+ this.name = "AuthActionNotAllowedError";
3151
+ this.actionType = actionType;
3152
+ }
3153
+ };
2981
3154
  var HashMismatchError = class extends Error {
2982
3155
  _scope;
2983
3156
  _document;
@@ -3004,6 +3177,25 @@ var HashMismatchError = class extends Error {
3004
3177
  return this._operation;
3005
3178
  }
3006
3179
  };
3180
+ /**
3181
+ * Thrown when replay or import requires a document model version that is not
3182
+ * registered. Carries the data the UI needs to explain the mismatch.
3183
+ */
3184
+ var UnsupportedDocumentModelVersionError = class extends Error {
3185
+ documentType;
3186
+ requiredVersion;
3187
+ availableVersions;
3188
+ constructor(documentType, requiredVersion, availableVersions) {
3189
+ super(`No reducer registered for document version ${requiredVersion}. Available versions: ${availableVersions.join(", ")}`);
3190
+ this.name = "UnsupportedDocumentModelVersionError";
3191
+ this.documentType = documentType;
3192
+ this.requiredVersion = requiredVersion;
3193
+ this.availableVersions = availableVersions;
3194
+ }
3195
+ static isError(error) {
3196
+ return Error.isError(error) && error.name === "UnsupportedDocumentModelVersionError";
3197
+ }
3198
+ };
3007
3199
  const isDefinedNonNullAny$1 = (v) => v !== void 0 && v !== null;
3008
3200
  any().refine((v) => isDefinedNonNullAny$1(v));
3009
3201
  const Load_StateSchema = _enum(["LOAD_STATE"]);
@@ -3444,6 +3636,88 @@ const ConfigEntrySchema = object({
3444
3636
  required: boolean().optional(),
3445
3637
  default: boolean().optional()
3446
3638
  });
3639
+ const PwaUrlPatternSchema = union([string(), strictObject({
3640
+ source: string(),
3641
+ flags: string().optional()
3642
+ }).refine((p) => {
3643
+ try {
3644
+ new RegExp(p.source, p.flags);
3645
+ return true;
3646
+ } catch {
3647
+ return false;
3648
+ }
3649
+ }, { message: "source/flags do not compile to a valid RegExp" })]);
3650
+ const PwaRuntimeCachingSchema = strictObject({
3651
+ urlPattern: PwaUrlPatternSchema,
3652
+ handler: _enum([
3653
+ "CacheFirst",
3654
+ "CacheOnly",
3655
+ "NetworkFirst",
3656
+ "NetworkOnly",
3657
+ "StaleWhileRevalidate"
3658
+ ]),
3659
+ method: _enum([
3660
+ "GET",
3661
+ "POST",
3662
+ "PUT",
3663
+ "DELETE",
3664
+ "HEAD",
3665
+ "PATCH"
3666
+ ]).optional(),
3667
+ options: strictObject({
3668
+ cacheName: string().optional(),
3669
+ networkTimeoutSeconds: number().optional(),
3670
+ expiration: strictObject({
3671
+ maxEntries: number().optional(),
3672
+ maxAgeSeconds: number().optional()
3673
+ }).optional(),
3674
+ cacheableResponse: strictObject({
3675
+ statuses: array(number()).optional(),
3676
+ headers: record(string(), string()).optional()
3677
+ }).optional()
3678
+ }).optional()
3679
+ });
3680
+ const PwaIconSchema = strictObject({
3681
+ src: string(),
3682
+ sizes: string().optional(),
3683
+ type: string().optional(),
3684
+ purpose: string().optional()
3685
+ });
3686
+ const PwaFileHandlerSchema = strictObject({
3687
+ accept: record(string(), array(string().regex(/^\./, "file extensions must start with '.'"))),
3688
+ icons: array(PwaIconSchema).optional(),
3689
+ launch_type: _enum(["single-client", "multiple-clients"]).optional()
3690
+ });
3691
+ const PwaConfigSchema = strictObject({
3692
+ manifest: strictObject({
3693
+ name: string().optional(),
3694
+ short_name: string().optional(),
3695
+ description: string().optional(),
3696
+ theme_color: string().optional(),
3697
+ background_color: string().optional(),
3698
+ display: _enum([
3699
+ "fullscreen",
3700
+ "standalone",
3701
+ "minimal-ui",
3702
+ "browser"
3703
+ ]).optional(),
3704
+ start_url: string().optional(),
3705
+ scope: string().optional(),
3706
+ icons: array(PwaIconSchema).optional(),
3707
+ file_handlers: array(PwaFileHandlerSchema).optional(),
3708
+ launch_handler: strictObject({ client_mode: _enum([
3709
+ "auto",
3710
+ "focus-existing",
3711
+ "navigate-existing",
3712
+ "navigate-new"
3713
+ ]) }).optional()
3714
+ }).optional(),
3715
+ globPatterns: array(string()).optional(),
3716
+ globIgnores: array(string()).optional(),
3717
+ maximumFileSizeToCacheInBytes: number().optional(),
3718
+ runtimeCaching: array(PwaRuntimeCachingSchema).optional(),
3719
+ navigateFallbackDenylist: array(PwaUrlPatternSchema).optional()
3720
+ });
3447
3721
  object({
3448
3722
  name: string(),
3449
3723
  description: string().optional(),
@@ -3455,7 +3729,8 @@ object({
3455
3729
  editors: PowerhouseModulesSchema,
3456
3730
  processors: PowerhouseModulesSchema,
3457
3731
  subgraphs: PowerhouseModulesSchema,
3458
- config: array(ConfigEntrySchema).optional()
3732
+ config: array(ConfigEntrySchema).optional(),
3733
+ pwa: PwaConfigSchema.optional()
3459
3734
  });
3460
3735
  /**
3461
3736
  * Cancels the last `count` operations.
@@ -3726,6 +4001,505 @@ const actions = {
3726
4001
  ...baseActions,
3727
4002
  ...documentModelActions
3728
4003
  };
4004
+ const documentModelDocumentType = "powerhouse/document-model";
4005
+ /**
4006
+ * Thrown when a grant violates the v1 validation rules. The message is stored
4007
+ * on error operations, so it must be a pure function of the input.
4008
+ */
4009
+ var InvalidGrantError = class extends Error {
4010
+ grantId;
4011
+ constructor(grantId, problem) {
4012
+ super(`Invalid grant "${grantId}": ${problem}`);
4013
+ this.name = "InvalidGrantError";
4014
+ this.grantId = grantId;
4015
+ }
4016
+ };
4017
+ /** Thrown for a `{ group }` principal on a group document: references never chain. */
4018
+ var GroupPrincipalNotAllowedError = class extends Error {
4019
+ grantId;
4020
+ constructor(grantId) {
4021
+ super(`Grant "${grantId}" uses a group principal on a group document: a group's auth scope cannot reference other groups`);
4022
+ this.name = "GroupPrincipalNotAllowedError";
4023
+ this.grantId = grantId;
4024
+ }
4025
+ };
4026
+ /**
4027
+ * Thrown when a change would leave a creator-less policy with no grant
4028
+ * permitting execute on the auth scope. Without the creator carve-out no
4029
+ * subject could ever administer such a policy again.
4030
+ */
4031
+ var AuthAdministrationLockoutError = class extends Error {
4032
+ grantId;
4033
+ constructor(grantId) {
4034
+ 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`);
4035
+ this.name = "AuthAdministrationLockoutError";
4036
+ this.grantId = grantId;
4037
+ }
4038
+ };
4039
+ /**
4040
+ * Thrown when INITIALIZE_AUTH would create a creator-less policy with no grant
4041
+ * permitting execute on the auth scope. Such a policy would be born with no
4042
+ * subject able to administer it.
4043
+ */
4044
+ var AuthAdministrationMissingError = class extends Error {
4045
+ constructor() {
4046
+ super("Initial grants include no reachable grant permitting execute on the auth scope: a policy with no creator must always include one");
4047
+ this.name = "AuthAdministrationMissingError";
4048
+ }
4049
+ };
4050
+ const GRANT_KEYS = new Set([
4051
+ "id",
4052
+ "description",
4053
+ "effect",
4054
+ "principal",
4055
+ "capability",
4056
+ "where"
4057
+ ]);
4058
+ const PRINCIPAL_KINDS = new Set([
4059
+ "anyone",
4060
+ "address",
4061
+ "group",
4062
+ "match"
4063
+ ]);
4064
+ const COMPARISON_CONDITION_KINDS = new Set([
4065
+ "eq",
4066
+ "ne",
4067
+ "lt",
4068
+ "lte",
4069
+ "gt",
4070
+ "gte"
4071
+ ]);
4072
+ function isPlainValue(value) {
4073
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4074
+ }
4075
+ function operandProblem(value, capabilityScope, budget) {
4076
+ budget.nodes -= 1;
4077
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
4078
+ if (!isPlainValue(value)) return "operand must be an object";
4079
+ const keys = Object.keys(value);
4080
+ if (keys.length !== 1) return "operand must have exactly one of attr or lit";
4081
+ if (keys[0] === "attr") {
4082
+ const attr = value.attr;
4083
+ if (typeof attr !== "string" || attr.length === 0) return "attr must be a non-empty string";
4084
+ if (capabilityScope !== void 0 && capabilityScope !== "*" && attr.startsWith("doc.")) {
4085
+ const pathScope = attr.split(".")[1] ?? "";
4086
+ if (pathScope !== capabilityScope) return `condition path "${attr}" reads scope "${pathScope}" but the capability covers only scope "${capabilityScope}"`;
4087
+ }
4088
+ return null;
4089
+ }
4090
+ if (keys[0] === "lit") {
4091
+ const lit = value.lit;
4092
+ if (lit !== null && typeof lit !== "string" && typeof lit !== "number" && typeof lit !== "boolean") return "lit must be a string, number, boolean, or null";
4093
+ if (typeof lit === "number" && !Number.isFinite(lit)) return "lit must be a finite number";
4094
+ if (typeof lit === "number" && Object.is(lit, -0)) return "lit must not be negative zero";
4095
+ return null;
4096
+ }
4097
+ return `unknown operand kind "${keys[0]}"`;
4098
+ }
4099
+ function conditionProblem(value, capabilityScope, depth, budget) {
4100
+ if (depth > 10) return `condition exceeds depth 10`;
4101
+ budget.nodes -= 1;
4102
+ if (budget.nodes < 0) return `condition exceeds 100 nodes`;
4103
+ if (!isPlainValue(value)) return "condition must be an object";
4104
+ const keys = Object.keys(value);
4105
+ if (keys.length !== 1) return "condition must have exactly one operator";
4106
+ const kind = keys[0];
4107
+ const body = value[kind];
4108
+ if (COMPARISON_CONDITION_KINDS.has(kind)) {
4109
+ if (!Array.isArray(body) || body.length !== 2) return `${kind} requires a pair of operands`;
4110
+ for (const operand of body) {
4111
+ const problem = operandProblem(operand, capabilityScope, budget);
4112
+ if (problem !== null) return problem;
4113
+ }
4114
+ return null;
4115
+ }
4116
+ if (kind === "in" || kind === "notIn") {
4117
+ if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return `${kind} requires an operand and an operand list`;
4118
+ const first = operandProblem(body[0], capabilityScope, budget);
4119
+ if (first !== null) return first;
4120
+ for (const operand of body[1]) {
4121
+ const problem = operandProblem(operand, capabilityScope, budget);
4122
+ if (problem !== null) return problem;
4123
+ }
4124
+ return null;
4125
+ }
4126
+ if (kind === "exists") return operandProblem(body, capabilityScope, budget);
4127
+ if (kind === "and" || kind === "or") {
4128
+ if (!Array.isArray(body)) return `${kind} requires a condition list`;
4129
+ for (const child of body) {
4130
+ const problem = conditionProblem(child, capabilityScope, depth + 1, budget);
4131
+ if (problem !== null) return problem;
4132
+ }
4133
+ return null;
4134
+ }
4135
+ if (kind === "not") return conditionProblem(body, capabilityScope, depth + 1, budget);
4136
+ return `unknown condition operator "${kind}"`;
4137
+ }
4138
+ function principalProblem(value, capabilityScope) {
4139
+ if (!isPlainValue(value)) return "principal must be an object";
4140
+ const keys = Object.keys(value);
4141
+ if (keys.length !== 1 || !PRINCIPAL_KINDS.has(keys[0])) return "principal must have exactly one of anyone, address, group, or match";
4142
+ const kind = keys[0];
4143
+ if (kind === "anyone" && value.anyone !== true) return "anyone must be true";
4144
+ if (kind === "address") {
4145
+ const address = value.address;
4146
+ if (typeof address !== "string" || address.length === 0) return "address must be a non-empty string";
4147
+ }
4148
+ if (kind === "group") {
4149
+ const group = value.group;
4150
+ if (typeof group !== "string" || group.length === 0) return "group must be a non-empty string";
4151
+ }
4152
+ if (kind === "match") return conditionProblem(value.match, capabilityScope, 1, { nodes: 100 });
4153
+ return null;
4154
+ }
4155
+ function capabilityProblem(value) {
4156
+ if (!isPlainValue(value)) return "capability must be an object";
4157
+ const can = value.can;
4158
+ if (can !== "read" && can !== "execute") return "capability.can must be read or execute";
4159
+ const allowedKeys = can === "execute" ? [
4160
+ "can",
4161
+ "scope",
4162
+ "operation"
4163
+ ] : ["can", "scope"];
4164
+ const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key)).sort();
4165
+ if (unknownKeys.length > 0) return `unknown capability key "${unknownKeys[0]}"`;
4166
+ if (value.scope !== void 0) {
4167
+ if (typeof value.scope !== "string" || value.scope.length === 0) return "capability.scope must be a non-empty string";
4168
+ }
4169
+ if (can === "execute" && value.operation !== void 0) {
4170
+ const operation = value.operation;
4171
+ if (!Array.isArray(operation)) return "capability.operation must be an array";
4172
+ if (operation.length > 100) return `capability.operation exceeds 100 entries`;
4173
+ for (const entry of operation) if (typeof entry !== "string" || entry.length === 0) return "capability.operation entries must be non-empty strings";
4174
+ }
4175
+ return null;
4176
+ }
4177
+ /** Returns the first v1-rule violation, or null. Pure, total, deterministic. */
4178
+ function grantProblem(value) {
4179
+ if (!isPlainValue(value)) return "grant must be an object";
4180
+ const unknownKeys = Object.keys(value).filter((key) => !GRANT_KEYS.has(key)).sort();
4181
+ if (unknownKeys.length > 0) return `unknown grant key "${unknownKeys[0]}"`;
4182
+ if (typeof value.id !== "string" || value.id.length === 0) return "id must be a non-empty string";
4183
+ if (typeof value.description !== "string") return "description must be a string";
4184
+ if (value.effect !== "allow" && value.effect !== "deny") return "effect must be allow or deny";
4185
+ const capabilityValue = value.capability;
4186
+ const capability = capabilityProblem(capabilityValue);
4187
+ if (capability !== null) return capability;
4188
+ const capabilityScope = capabilityValue.scope;
4189
+ const principal = principalProblem(value.principal, capabilityScope);
4190
+ if (principal !== null) return principal;
4191
+ if (value.where !== void 0) return conditionProblem(value.where, capabilityScope, 1, { nodes: 100 });
4192
+ return null;
4193
+ }
4194
+ /** V1 shape rules plus the group-document group-principal ban. */
4195
+ function assertValidGrant(grant, documentType) {
4196
+ const grantId = isPlainValue(grant) && typeof grant.id === "string" ? grant.id : "";
4197
+ const problem = grantProblem(grant);
4198
+ if (problem !== null) throw new InvalidGrantError(grantId, problem);
4199
+ if (documentType === "powerhouse/reactor-group" && "group" in grant.principal) throw new GroupPrincipalNotAllowedError(grantId);
4200
+ }
4201
+ /**
4202
+ * Validates an initial grant list: the count cap, every grant, and — on a
4203
+ * creator-less policy — that some grant keeps the auth scope administrable.
4204
+ */
4205
+ function assertValidInitialGrants(grants, documentType, creator) {
4206
+ if (grants.length > 100) throw new InvalidGrantError("", `policy exceeds 100 grants`);
4207
+ for (const grant of grants) assertValidGrant(grant, documentType);
4208
+ if (creator === void 0 && !administrationReachable(grants)) throw new AuthAdministrationMissingError();
4209
+ }
4210
+ /**
4211
+ * Validates a grant upsert: the grant itself, the count cap on append, and
4212
+ * administration retention. Retention is checked on an append as well as an
4213
+ * in-place replace, because a grant appended after the administration grant can
4214
+ * shadow it (evaluation is last-applicable-grant-wins) and so take
4215
+ * administration away without removing anything.
4216
+ */
4217
+ function assertValidGrantUpsert(grant, existing, documentType, creator) {
4218
+ assertValidGrant(grant, documentType);
4219
+ const exists = existing.some((g) => g.id === grant.id);
4220
+ if (!exists && existing.length >= 100) throw new InvalidGrantError(grant.id, `policy exceeds 100 grants`);
4221
+ assertAuthAdministrationRetained(creator, existing, exists ? existing.map((g) => g.id === grant.id ? grant : g) : [...existing, grant], grant.id);
4222
+ }
4223
+ /**
4224
+ * The request whose coverage keeps a policy administrable: a subject who may
4225
+ * SET_GRANT can upsert any grant, so every other repair stays reachable.
4226
+ */
4227
+ const AUTH_ADMINISTRATION_REQUEST = {
4228
+ verb: "execute",
4229
+ scope: "auth",
4230
+ operation: "SET_GRANT"
4231
+ };
4232
+ /**
4233
+ * Whether some subject can still administer the auth scope under this grant list.
4234
+ *
4235
+ * Asks the evaluator rather than pattern-matching a single grant: evaluation is
4236
+ * last-applicable-grant-wins, so an allow that some later deny shadows keeps
4237
+ * nothing reachable even though it is still present in the list. Only anyone and
4238
+ * address principals are candidates, because those are the ones v1 can match at
4239
+ * all; a `where` condition or a group or match principal never applies.
4240
+ */
4241
+ function administrationReachable(grants) {
4242
+ return grants.some((grant) => {
4243
+ const subject = administrationCandidate(grant);
4244
+ if (subject === void 0) return false;
4245
+ return evaluateGrantStack(grants, subject, AUTH_ADMINISTRATION_REQUEST).decision === "allow";
4246
+ });
4247
+ }
4248
+ /**
4249
+ * The subject to test this grant with, or undefined when the grant could never
4250
+ * carry administration for anyone.
4251
+ */
4252
+ function administrationCandidate(grant) {
4253
+ if (grant.effect !== "allow" || grant.where !== void 0 || !capabilityCovers(grant.capability, AUTH_ADMINISTRATION_REQUEST)) return;
4254
+ if ("address" in grant.principal) return {
4255
+ address: grant.principal.address,
4256
+ key: void 0
4257
+ };
4258
+ if ("anyone" in grant.principal) return {
4259
+ address: void 0,
4260
+ key: void 0
4261
+ };
4262
+ }
4263
+ /**
4264
+ * A creator-less policy must always retain a grant permitting execute on the
4265
+ * auth scope; on a signed document the creator carve-out keeps administration
4266
+ * reachable instead. Rejects a change that takes the last such grant away. A
4267
+ * policy already without one is left alone: the change is not what locks it.
4268
+ */
4269
+ function assertAuthAdministrationRetained(creator, previous, next, grantId) {
4270
+ if (creator !== void 0) return;
4271
+ if (administrationReachable(previous) && !administrationReachable(next)) throw new AuthAdministrationLockoutError(grantId);
4272
+ }
4273
+ /**
4274
+ * An operand whose shape validation would have rejected. Distinguished from
4275
+ * an unresolved path so a structurally malformed operand poisons its whole
4276
+ * condition to false rather than reading as "absent", which `not` would
4277
+ * otherwise widen to true.
4278
+ */
4279
+ const INVALID_OPERAND = Symbol("invalid-operand");
4280
+ /**
4281
+ * Narrows to the values conditions compare. An object, array, or non-finite
4282
+ * number resolves to undefined, and every comparison involving undefined is
4283
+ * false.
4284
+ */
4285
+ function asConditionValue(value) {
4286
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
4287
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4288
+ }
4289
+ /**
4290
+ * Resolves one operand. Attr roots: `subject.*`, `doc.<scope>.*` where the
4291
+ * scope must be the executing scope (validation already rejects any other,
4292
+ * but resolution stays total), and `action.input.*`. Path steps read own
4293
+ * properties only, so prototype members can never influence a verdict.
4294
+ */
4295
+ function resolveOperand(operand, subject, request, conditions) {
4296
+ if (!isPlainValue(operand)) return INVALID_OPERAND;
4297
+ const keys = Object.keys(operand);
4298
+ if (keys.length !== 1) return INVALID_OPERAND;
4299
+ if (keys[0] === "lit") {
4300
+ const value = asConditionValue(operand.lit);
4301
+ return value === void 0 ? INVALID_OPERAND : value;
4302
+ }
4303
+ if (keys[0] !== "attr") return INVALID_OPERAND;
4304
+ const attr = operand.attr;
4305
+ if (typeof attr !== "string" || attr.length === 0) return INVALID_OPERAND;
4306
+ const path = attr.split(".");
4307
+ let value;
4308
+ let rest;
4309
+ if (path[0] === "subject") {
4310
+ value = subject;
4311
+ rest = path.slice(1);
4312
+ } else if (path[0] === "doc") {
4313
+ if (path[1] !== request.scope) return;
4314
+ value = conditions.scopeState;
4315
+ rest = path.slice(2);
4316
+ } else if (path[0] === "action" && path[1] === "input") {
4317
+ value = conditions.actionInput;
4318
+ rest = path.slice(2);
4319
+ } else return;
4320
+ for (const segment of rest) {
4321
+ if (!isPlainValue(value) || !Object.hasOwn(value, segment)) return;
4322
+ value = value[segment];
4323
+ }
4324
+ return asConditionValue(value);
4325
+ }
4326
+ /**
4327
+ * Total order within one type: numbers numerically, strings by code point.
4328
+ * Everything else, including mixed types, does not order.
4329
+ */
4330
+ function compareValues(left, right) {
4331
+ if (typeof left === "number" && typeof right === "number") return left < right ? -1 : left > right ? 1 : 0;
4332
+ if (typeof left === "string" && typeof right === "string") {
4333
+ const leftPoints = Array.from(left);
4334
+ const rightPoints = Array.from(right);
4335
+ const shared = Math.min(leftPoints.length, rightPoints.length);
4336
+ for (let i = 0; i < shared; i++) {
4337
+ const a = leftPoints[i].codePointAt(0) ?? 0;
4338
+ const b = rightPoints[i].codePointAt(0) ?? 0;
4339
+ if (a !== b) return a < b ? -1 : 1;
4340
+ }
4341
+ return leftPoints.length === rightPoints.length ? 0 : leftPoints.length < rightPoints.length ? -1 : 1;
4342
+ }
4343
+ }
4344
+ /**
4345
+ * Tri-state evaluation: undefined marks a structurally invalid node, which
4346
+ * poisons the whole tree to false at the top — and a structurally malformed
4347
+ * operand poisons its condition the same way. Both are distinct from an
4348
+ * operand whose path fails to resolve, which is a valid comparison that is
4349
+ * false. The distinction keeps `not` from widening over malformed input.
4350
+ */
4351
+ function evaluateNode(node, subject, request, conditions) {
4352
+ if (!isPlainValue(node)) return;
4353
+ const keys = Object.keys(node);
4354
+ if (keys.length !== 1) return;
4355
+ const kind = keys[0];
4356
+ const body = node[kind];
4357
+ switch (kind) {
4358
+ case "eq":
4359
+ case "ne":
4360
+ case "lt":
4361
+ case "lte":
4362
+ case "gt":
4363
+ case "gte": {
4364
+ if (!Array.isArray(body) || body.length !== 2) return;
4365
+ const left = resolveOperand(body[0], subject, request, conditions);
4366
+ const right = resolveOperand(body[1], subject, request, conditions);
4367
+ if (left === INVALID_OPERAND || right === INVALID_OPERAND) return;
4368
+ if (left === void 0 || right === void 0) return false;
4369
+ if (kind === "eq") return left === right;
4370
+ if (kind === "ne") return left !== right;
4371
+ const order = compareValues(left, right);
4372
+ if (order === void 0) return false;
4373
+ switch (kind) {
4374
+ case "lt": return order < 0;
4375
+ case "lte": return order <= 0;
4376
+ case "gt": return order > 0;
4377
+ case "gte": return order >= 0;
4378
+ }
4379
+ return;
4380
+ }
4381
+ case "in":
4382
+ case "notIn": {
4383
+ if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return;
4384
+ const left = resolveOperand(body[0], subject, request, conditions);
4385
+ if (left === INVALID_OPERAND) return;
4386
+ const elements = body[1].map((element) => resolveOperand(element, subject, request, conditions));
4387
+ if (elements.some((value) => value === INVALID_OPERAND)) return;
4388
+ if (left === void 0) return false;
4389
+ const found = elements.some((value) => value !== void 0 && value === left);
4390
+ return kind === "in" ? found : !found;
4391
+ }
4392
+ case "exists": {
4393
+ const value = resolveOperand(body, subject, request, conditions);
4394
+ if (value === INVALID_OPERAND) return;
4395
+ return value !== void 0;
4396
+ }
4397
+ case "and":
4398
+ case "or": {
4399
+ if (!Array.isArray(body)) return;
4400
+ let result = kind === "and";
4401
+ for (const child of body) {
4402
+ const value = evaluateNode(child, subject, request, conditions);
4403
+ if (value === void 0) return;
4404
+ if (kind === "and") result = result && value;
4405
+ else result = result || value;
4406
+ }
4407
+ return result;
4408
+ }
4409
+ case "not": {
4410
+ const value = evaluateNode(body, subject, request, conditions);
4411
+ return value === void 0 ? void 0 : !value;
4412
+ }
4413
+ default: return;
4414
+ }
4415
+ }
4416
+ /**
4417
+ * Evaluates a version-1 condition. Deterministic, total, and pure: any input
4418
+ * shape yields a boolean and never throws, and a malformed condition is
4419
+ * false. These are consensus semantics, versioned by `PHAuthState.version`;
4420
+ * changing them requires a new version.
4421
+ */
4422
+ function evaluateCondition(condition, subject, request, conditions) {
4423
+ return evaluateNode(condition, subject, request, conditions) === true;
4424
+ }
4425
+ /** Whether a capability's scope reaches the requested one. */
4426
+ function scopeCovers(scope, requested) {
4427
+ return scope === void 0 || scope === "*" || scope === requested;
4428
+ }
4429
+ /**
4430
+ * Whether one grant answers this request.
4431
+ *
4432
+ * A grant allowing execute on a scope also allows reading it: executing an
4433
+ * operation means reading the state it applies to, so permitting the write while
4434
+ * withholding the read would describe an access nobody could use. The converse
4435
+ * does not hold -- a read grant confers no write.
4436
+ *
4437
+ * Only an allow carries across. A deny on execute withholds the write and says
4438
+ * nothing about the read, so a policy locking writes down does not silently
4439
+ * revoke a read grant sitting before it. The operation list is not consulted
4440
+ * either: it restricts which operations may be executed, not whether the scope
4441
+ * is visible, and a read carries no operation to match against.
4442
+ *
4443
+ * Reads are not consensus -- no replica records a read, and the two read call
4444
+ * sites are both inside the read gate -- so this rule is not part of what makes
4445
+ * an operation valid, and does not need a policy version of its own.
4446
+ */
4447
+ function grantAnswers(grant, request) {
4448
+ if (capabilityCovers(grant.capability, request)) return true;
4449
+ return request.verb === "read" && grant.effect === "allow" && grant.capability.can === "execute" && scopeCovers(grant.capability.scope, request.scope);
4450
+ }
4451
+ function capabilityCovers(capability, request) {
4452
+ if (capability.can !== request.verb) return false;
4453
+ if (!scopeCovers(capability.scope, request.scope)) return false;
4454
+ if (capability.can === "execute") {
4455
+ if (capability.operation === void 0) return true;
4456
+ return request.operation !== void 0 && capability.operation.includes(request.operation);
4457
+ }
4458
+ return true;
4459
+ }
4460
+ function principalMatches(principal, subject, request, groups, conditions) {
4461
+ if ("anyone" in principal) return true;
4462
+ if ("address" in principal) return subject.address !== void 0 && subject.address.toLowerCase() === principal.address.toLowerCase();
4463
+ if ("group" in principal) {
4464
+ if (groups === void 0 || subject.address === void 0) return false;
4465
+ const group = groups[principal.group];
4466
+ if (group === void 0 || !Array.isArray(group.members)) return false;
4467
+ const address = subject.address.toLowerCase();
4468
+ return group.members.some((member) => member.toLowerCase() === address);
4469
+ }
4470
+ if ("match" in principal) {
4471
+ if (conditions === void 0) return false;
4472
+ return evaluateCondition(principal.match, subject, request, conditions);
4473
+ }
4474
+ return false;
4475
+ }
4476
+ /**
4477
+ * Evaluates a v1 grant stack: default deny, last applicable grant wins, and
4478
+ * reports which grant decided it. Group principals match only against a
4479
+ * supplied groups map, and `where` clauses and { match } principals evaluate
4480
+ * only against a supplied condition context; a grant that uses an unsupplied
4481
+ * feature never applies.
4482
+ */
4483
+ function evaluateGrantStack(grants, subject, request, groups, conditions) {
4484
+ let applicable;
4485
+ for (const grant of grants) {
4486
+ if (grant.where !== void 0) {
4487
+ if (conditions === void 0) continue;
4488
+ if (!evaluateCondition(grant.where, subject, request, conditions)) continue;
4489
+ }
4490
+ if (grantAnswers(grant, request) && principalMatches(grant.principal, subject, request, groups, conditions)) applicable = grant;
4491
+ }
4492
+ if (applicable === void 0) return {
4493
+ decision: "deny",
4494
+ refusal: "no-applicable-grant"
4495
+ };
4496
+ if (applicable.effect === "deny") return {
4497
+ decision: "deny",
4498
+ refusal: "denied-by-grant",
4499
+ grantId: applicable.id
4500
+ };
4501
+ return { decision: "allow" };
4502
+ }
3729
4503
  const documentModelFileExtension = "phdm";
3730
4504
  const documentModelInitialLocalState = {};
3731
4505
  const documentModelInitialGlobalState = {
@@ -4307,7 +5081,306 @@ const documentModelGlobalState = {
4307
5081
  };
4308
5082
  const HASH_ALGORITHM_SHA1 = "sha1";
4309
5083
  const HASH_ENCODING_BASE64 = "base64";
4310
- const documentModelDocumentType = "powerhouse/document-model";
5084
+ /**
5085
+ * Creates a default PHAuthState
5086
+ */
5087
+ function defaultAuthState() {
5088
+ return {
5089
+ version: 0,
5090
+ grants: []
5091
+ };
5092
+ }
5093
+ /**
5094
+ * Creates a default PHDocumentState
5095
+ */
5096
+ function defaultDocumentState() {
5097
+ return {
5098
+ version: 0,
5099
+ hash: {
5100
+ algorithm: HASH_ALGORITHM_SHA1,
5101
+ encoding: HASH_ENCODING_BASE64
5102
+ }
5103
+ };
5104
+ }
5105
+ /**
5106
+ * Creates a default PHBaseState with auth and document properties
5107
+ */
5108
+ function defaultBaseState() {
5109
+ return {
5110
+ auth: defaultAuthState(),
5111
+ document: defaultDocumentState()
5112
+ };
5113
+ }
5114
+ /**
5115
+ * Creates a PHAuthState with the given properties
5116
+ */
5117
+ function createAuthState(auth) {
5118
+ return {
5119
+ ...defaultAuthState(),
5120
+ ...auth
5121
+ };
5122
+ }
5123
+ /**
5124
+ * Creates a PHDocumentState with the given properties
5125
+ */
5126
+ function createDocumentState(document) {
5127
+ return {
5128
+ ...defaultDocumentState(),
5129
+ ...document
5130
+ };
5131
+ }
5132
+ /**
5133
+ * Creates a PHBaseState with the given auth and document properties
5134
+ */
5135
+ function createBaseState(auth, document) {
5136
+ return {
5137
+ auth: createAuthState(auth),
5138
+ document: createDocumentState(document)
5139
+ };
5140
+ }
5141
+ /**
5142
+ * Backfills the auth scope to the default for legacy documents serialized with
5143
+ * an empty `auth`. Replaces only `state.auth`. Idempotent.
5144
+ */
5145
+ function backfillAuthState(state) {
5146
+ return {
5147
+ ...state,
5148
+ auth: createAuthState(state.auth)
5149
+ };
5150
+ }
5151
+ function defaultGlobalState() {
5152
+ return {
5153
+ ...defaultBaseState(),
5154
+ author: {
5155
+ name: "",
5156
+ website: ""
5157
+ },
5158
+ description: "",
5159
+ extension: "",
5160
+ id: "",
5161
+ name: "",
5162
+ specifications: []
5163
+ };
5164
+ }
5165
+ function defaultLocalState() {
5166
+ return {};
5167
+ }
5168
+ function createGlobalState(state) {
5169
+ return {
5170
+ ...defaultGlobalState(),
5171
+ ...state || {}
5172
+ };
5173
+ }
5174
+ function createLocalState(state) {
5175
+ return {
5176
+ ...defaultLocalState(),
5177
+ ...state || {}
5178
+ };
5179
+ }
5180
+ function createState(baseState, globalState, localState) {
5181
+ return {
5182
+ ...createBaseState(baseState?.auth, baseState?.document),
5183
+ global: createGlobalState(globalState),
5184
+ local: createLocalState(localState)
5185
+ };
5186
+ }
5187
+ /**
5188
+ * Destructuring a null input (reachable via raw synced operations) would
5189
+ * store an engine-specific TypeError message on the error operation.
5190
+ */
5191
+ function assertActionInputShape(input) {
5192
+ if (!isPlainValue(input)) throw new InvalidActionInputError({ input: "must be an object" });
5193
+ }
5194
+ function withGrants(document, grants) {
5195
+ return {
5196
+ ...document,
5197
+ state: {
5198
+ ...document.state,
5199
+ auth: {
5200
+ ...document.state.auth,
5201
+ grants
5202
+ }
5203
+ }
5204
+ };
5205
+ }
5206
+ const P256_PUBKEY_MULTICODEC = [128, 36];
5207
+ const DID_KEY_PREFIX = "did:key:z";
5208
+ function bytesEqual(a, b) {
5209
+ if (a.length !== b.length) return false;
5210
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
5211
+ return true;
5212
+ }
5213
+ /**
5214
+ * True when `signerKey` (an ActionSigner app key, a did:key) identifies the same
5215
+ * key recorded as the document creator. Returns false
5216
+ * when there is no creator (empty JWK) or no signer key.
5217
+ */
5218
+ function isDocumentCreator(creatorKey, signerKey) {
5219
+ if (!creatorKey?.x || !creatorKey.y) return false;
5220
+ if (!signerKey || !signerKey.startsWith(DID_KEY_PREFIX)) return false;
5221
+ const decoded = base58Decode(signerKey.slice(9));
5222
+ if (!decoded || decoded.length !== 35) return false;
5223
+ if (decoded[0] !== P256_PUBKEY_MULTICODEC[0] || decoded[1] !== P256_PUBKEY_MULTICODEC[1]) return false;
5224
+ const parityPrefix = decoded[2];
5225
+ if (parityPrefix !== 2 && parityPrefix !== 3) return false;
5226
+ const didX = decoded.subarray(3, 35);
5227
+ const jwkX = base64UrlToBytes(creatorKey.x);
5228
+ const jwkY = base64UrlToBytes(creatorKey.y);
5229
+ if (jwkX.length !== 32 || jwkY.length !== 32) return false;
5230
+ if (!bytesEqual(didX, jwkX)) return false;
5231
+ return (jwkY[31] & 1) === 1 === (parityPrefix === 3);
5232
+ }
5233
+ /**
5234
+ * Sets the initial policy. Valid only while the auth scope is uninitialized
5235
+ * (version 0). The input version is the policy language version and must be an
5236
+ * integer >= 1; 0 is reserved for the uninitialized state. On a signed-header
5237
+ * document it must be signed by the document creator (`header.sig.publicKey`).
5238
+ * A creator-less policy must include a grant permitting execute on the auth
5239
+ * scope, or it would be born locked out.
5240
+ */
5241
+ function applyInitializeAuthAction(document, action) {
5242
+ assertActionInputShape(action.input);
5243
+ const { version, grants } = action.input;
5244
+ if (!Number.isInteger(version) || version < 1) throw new InvalidAuthVersionError(document.header.id, version);
5245
+ if (document.state.auth.version !== 0) throw new AuthAlreadyInitializedError(document.header.id);
5246
+ if (!Array.isArray(grants)) throw new InvalidActionInputError({ grants: "must be an array" });
5247
+ const creatorKey = document.header.sig.publicKey;
5248
+ const signerKey = action.context?.signer?.app.key;
5249
+ const hasCreator = Boolean(creatorKey.kty || creatorKey.x || creatorKey.y);
5250
+ if (hasCreator && !isDocumentCreator(creatorKey, signerKey)) throw new AuthInitializerNotCreatorError(document.header.id);
5251
+ const creator = hasCreator ? signerKey : void 0;
5252
+ assertValidInitialGrants(grants, document.header.documentType, creator);
5253
+ return {
5254
+ ...document,
5255
+ state: {
5256
+ ...document.state,
5257
+ auth: createAuthState(creator ? {
5258
+ version,
5259
+ grants,
5260
+ creator
5261
+ } : {
5262
+ version,
5263
+ grants
5264
+ })
5265
+ }
5266
+ };
5267
+ }
5268
+ /** Upserts a grant by id: replaces in place if present, otherwise appends. */
5269
+ function applySetGrantAction(document, action) {
5270
+ assertActionInputShape(action.input);
5271
+ const { grant } = action.input;
5272
+ const grants = document.state.auth.grants;
5273
+ assertValidGrantUpsert(grant, grants, document.header.documentType, document.state.auth.creator);
5274
+ return withGrants(document, grants.some((g) => g.id === grant.id) ? grants.map((g) => g.id === grant.id ? grant : g) : [...grants, grant]);
5275
+ }
5276
+ /**
5277
+ * Removes a grant by id; throws if the id is not present or if the removal
5278
+ * would leave a creator-less policy with no auth-administration grant.
5279
+ */
5280
+ function applyRemoveGrantAction(document, action) {
5281
+ assertActionInputShape(action.input);
5282
+ const { id } = action.input;
5283
+ const { grants, creator } = document.state.auth;
5284
+ if (!grants.some((g) => g.id === id)) throw new GrantNotFoundError(id);
5285
+ const next = grants.filter((g) => g.id !== id);
5286
+ assertAuthAdministrationRetained(creator, grants, next, id);
5287
+ return withGrants(document, next);
5288
+ }
5289
+ /**
5290
+ * Moves a grant by id to a new index. Order is load-bearing (the last
5291
+ * applicable grant wins), so the relative order of the other grants is kept.
5292
+ * The target index is clamped to the valid range; an unknown id throws.
5293
+ *
5294
+ * Order alone decides which grant wins, so a move can take administration away
5295
+ * without changing the list's contents. It carries the same retention rule as
5296
+ * the two mutation paths.
5297
+ */
5298
+ function applyMoveGrantAction(document, action) {
5299
+ assertActionInputShape(action.input);
5300
+ const { id, index } = action.input;
5301
+ const { grants, creator } = document.state.auth;
5302
+ const from = grants.findIndex((g) => g.id === id);
5303
+ if (from === -1) throw new GrantNotFoundError(id);
5304
+ const next = [...grants];
5305
+ const [moved] = next.splice(from, 1);
5306
+ const to = Math.max(0, Math.min(index, next.length));
5307
+ next.splice(to, 0, moved);
5308
+ assertAuthAdministrationRetained(creator, grants, next, id);
5309
+ return withGrants(document, next);
5310
+ }
5311
+ /**
5312
+ * Dispatches an auth-scope action to its handler. This is the auth scope's
5313
+ * dedicated reducer: it is applied by the base reducer instead of the model
5314
+ * reducer, mirroring the document-scope platform handlers. Unknown types are a
5315
+ * no-op, matching the model-reducer default.
5316
+ */
5317
+ function applyAuthAction(document, action) {
5318
+ switch (action.type) {
5319
+ case "INITIALIZE_AUTH": return applyInitializeAuthAction(document, action);
5320
+ case "SET_GRANT": return applySetGrantAction(document, action);
5321
+ case "REMOVE_GRANT": return applyRemoveGrantAction(document, action);
5322
+ case "MOVE_GRANT": return applyMoveGrantAction(document, action);
5323
+ default: return document;
5324
+ }
5325
+ }
5326
+ /**
5327
+ * Because only creators can initialize auth scopes, we must verify that either
5328
+ * the document has no auth or the version and creator match.
5329
+ */
5330
+ function assertAuthPreservedOnDuplicate(documentId, source, duplicated) {
5331
+ if (!source || source.version === 0) return;
5332
+ if (duplicated === void 0 || duplicated.version !== source.version || duplicated.creator !== source.creator) throw new AuthPolicyNotPreservedError(documentId);
5333
+ }
5334
+ /**
5335
+ * The auth scope a state snapshot may install, given the policy already there.
5336
+ *
5337
+ * `applyAuthAction` is the validated door onto `state.auth`, but a whole-state
5338
+ * snapshot (UPGRADE_DOCUMENT's `initialState`, LOAD_STATE's `data`) replaces the
5339
+ * scope wholesale and is authorized as a `document`-scope write. Without this,
5340
+ * a subject holding `execute` on `document` and no auth grant at all can install
5341
+ * a policy of its choosing, name itself `creator` (which exempts the policy from
5342
+ * the retention rule for good), or wipe an existing policy by carrying the
5343
+ * default uninitialized one.
5344
+ *
5345
+ * Three cases:
5346
+ *
5347
+ * - the snapshot carries no policy, or an uninitialized one: the document
5348
+ * keeps the policy it has, so a default-state upgrade cannot reset it;
5349
+ * - the document is uninitialized and the snapshot carries a policy: the
5350
+ * policy is installed after the same validation genesis applies, so it
5351
+ * cannot be born locked out;
5352
+ * - both carry a policy: they must agree. A duplicate preserves its source's
5353
+ * policy (see {@link assertAuthPreservedOnDuplicate}), and anything else is
5354
+ * an attempt to replace one policy with another.
5355
+ */
5356
+ function resolveSnapshotAuth(documentId, documentType, current, incoming) {
5357
+ const currentAuth = current ?? createAuthState({
5358
+ version: 0,
5359
+ grants: []
5360
+ });
5361
+ if (!incoming || !incoming.version) return currentAuth;
5362
+ if (currentAuth.version !== 0) {
5363
+ if (stringify$1(incoming) !== stringify$1(currentAuth)) throw new AuthPolicyNotPreservedError(documentId);
5364
+ return incoming;
5365
+ }
5366
+ if (!Array.isArray(incoming.grants)) throw new InvalidActionInputError({ grants: "must be an array" });
5367
+ assertValidInitialGrants(incoming.grants, documentType, incoming.creator);
5368
+ return incoming;
5369
+ }
5370
+ /** UNDO, REDO and PRUNE are rejected on the auth scope. */
5371
+ function assertAuthScopeActionAllowed(action) {
5372
+ if (action.scope === "auth" && [
5373
+ "UNDO",
5374
+ "REDO",
5375
+ "PRUNE"
5376
+ ].includes(action.type)) throw new AuthActionNotAllowedError(action.type);
5377
+ }
5378
+ /**
5379
+ * True iff authorization rejected the action.
5380
+ */
5381
+ function isDenied(operation) {
5382
+ return operation.deniedReason !== void 0;
5383
+ }
4311
5384
  const BaseDocumentHeaderSchema = object({
4312
5385
  id: string(),
4313
5386
  name: string(),
@@ -4438,6 +5511,7 @@ function createDocumentScopeOperations(header, state) {
4438
5511
  function baseCreateDocument(createState, initialState, documentType = "") {
4439
5512
  const state = createState(initialState);
4440
5513
  const header = createPresignedHeader(generateId(), documentType);
5514
+ header.protocolVersions = { "base-reducer": 2 };
4441
5515
  return {
4442
5516
  header,
4443
5517
  state,
@@ -4461,6 +5535,18 @@ function hashDocumentStateForScope(document, scope = "global") {
4461
5535
  * Uses the V2 model where consecutive NOOPs form chains.
4462
5536
  * Unlike V1 garbageCollect, this preserves ALL operations but marks which to apply.
4463
5537
  */
5538
+ /**
5539
+ * The base-reducer protocol version a document is written against. Every
5540
+ * document carries one, set from the CREATE_DOCUMENT input. A header without
5541
+ * one was built outside that path, and choosing a version on its behalf would
5542
+ * replay the document through a reducer it was not written for, so it is an
5543
+ * error rather than a default.
5544
+ */
5545
+ function baseReducerVersion(header) {
5546
+ const version = header.protocolVersions?.["base-reducer"];
5547
+ if (typeof version !== "number") throw new Error(`Document ${header.id} carries no base-reducer protocol version`);
5548
+ return version;
5549
+ }
4464
5550
  function garbageCollectV2(sortedOperations) {
4465
5551
  const result = [];
4466
5552
  let noopChainLength = 0;
@@ -4477,9 +5563,26 @@ function garbageCollectV2(sortedOperations) {
4477
5563
  const defaultCreateState = (state) => {
4478
5564
  return state;
4479
5565
  };
5566
+ /**
5567
+ * Records an operation in the history without applying it, which is what a
5568
+ * denied operation needs: it occupies its index and contributes no state.
5569
+ *
5570
+ * The scope defaults to the action's own, and is passed explicitly by a rebuild
5571
+ * that is walking one stream and does not want to trust the action's copy.
5572
+ */
5573
+ function appendWithoutApplying(document, operation, scope = operation.action.scope) {
5574
+ return {
5575
+ ...document,
5576
+ operations: {
5577
+ ...document.operations,
5578
+ [scope]: [...document.operations[scope] ?? [], operation]
5579
+ }
5580
+ };
5581
+ }
4480
5582
  function replayDocument(initialState, operations, reducer, header, dispatch, skipHeaderOperations = {}, options) {
4481
5583
  const { checkHashes = true, reuseOperationResultingState, operationResultingStateParser = parseResultingState, skipIndexValidation } = options || {};
4482
- let documentState = initialState;
5584
+ const backfilledInitialState = backfillAuthState(initialState);
5585
+ let documentState = backfilledInitialState;
4483
5586
  const operationsToReplay = [];
4484
5587
  const allScopes = new Set([
4485
5588
  ...Object.keys(operations),
@@ -4514,12 +5617,13 @@ function replayDocument(initialState, operations, reducer, header, dispatch, ski
4514
5617
  const document = {
4515
5618
  header,
4516
5619
  state: defaultCreateState(documentState),
4517
- initialState,
5620
+ initialState: backfilledInitialState,
4518
5621
  operations: initialOperations,
4519
5622
  clipboard: []
4520
5623
  };
4521
5624
  let result = document;
4522
5625
  if (operationsToReplay.length) result = operationsToReplay.reduce((document, operation) => {
5626
+ if (isDenied(operation)) return updateHeaderRevision(appendWithoutApplying(document, operation), operation.action.scope, operation.timestampUtcMs);
4523
5627
  return reducer(document, operation.action, dispatch, {
4524
5628
  ignoreSkipOperations: true,
4525
5629
  checkHashes,
@@ -4801,13 +5905,15 @@ function redoOperation(document, action, skip) {
4801
5905
  });
4802
5906
  }
4803
5907
  function loadStateOperation(document, action) {
5908
+ const loaded = backfillAuthState(action.state.data);
5909
+ loaded.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, loaded.auth);
4804
5910
  return {
4805
5911
  ...document,
4806
5912
  header: {
4807
5913
  ...document.header,
4808
5914
  name: action.state.name
4809
5915
  },
4810
- state: action.state.data
5916
+ state: loaded
4811
5917
  };
4812
5918
  }
4813
5919
  function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
@@ -4981,12 +6087,13 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
4981
6087
  function baseReducer(document, action, customReducer, dispatch, options = {}) {
4982
6088
  const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main" } = options;
4983
6089
  let _action = actionFromAction(action);
6090
+ assertAuthScopeActionAllowed(_action);
4984
6091
  let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
4985
6092
  let newDocument = { ...document };
4986
6093
  let reuseLastOperationIndex = false;
4987
6094
  const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
4988
6095
  if (isUndoRedo(_action)) {
4989
- const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? 1);
6096
+ const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? baseReducerVersion(document.header));
4990
6097
  _action = transformedAction;
4991
6098
  skipValue = calculatedSkip;
4992
6099
  newDocument = processedDocument;
@@ -5002,7 +6109,7 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5002
6109
  branch
5003
6110
  };
5004
6111
  newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
5005
- const protocolVersion = options.protocolVersion ?? 1;
6112
+ const protocolVersion = options.protocolVersion ?? baseReducerVersion(document.header);
5006
6113
  if (isUndo(action) && protocolVersion < 2) return processUndoOperation(newDocument, action.scope, customReducer);
5007
6114
  const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
5008
6115
  if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
@@ -5028,6 +6135,13 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
5028
6135
  }
5029
6136
  newDocument = create(newDocument, (draft) => {
5030
6137
  try {
6138
+ if (_action.scope === "auth") {
6139
+ const authState = applyAuthAction(newDocument, _action).state;
6140
+ unsafe(() => {
6141
+ draft.state = castDraft(authState);
6142
+ });
6143
+ return;
6144
+ }
5031
6145
  const newState = customReducer(draft.state, _action, dispatch);
5032
6146
  if (newState) unsafe(() => {
5033
6147
  draft.state = castDraft(newState);
@@ -5817,14 +6931,27 @@ const documentModelStateReducer = (state, action) => {
5817
6931
  }
5818
6932
  };
5819
6933
  const documentModelReducer = createReducer(documentModelStateReducer);
6934
+ /**
6935
+ * Canonical document-model version normalization: documents stamped with 0
6936
+ * or nothing at all predate versioning and are treated as version 1, the
6937
+ * same version the registry assigns unversioned modules. Every consumer
6938
+ * that resolves a module or compares versions must use this rule; resolving
6939
+ * 0 to "latest" instead re-pins a legacy document's history to whichever
6940
+ * module happens to be newest.
6941
+ */
6942
+ function normalizeDocumentModelVersion(version) {
6943
+ return version && version > 0 ? version : 1;
6944
+ }
5820
6945
  function applyInitialState(document, action) {
5821
6946
  const input = action.input;
5822
6947
  const newState = input.initialState || input.state;
5823
6948
  if (newState) {
5824
- document.state = {
6949
+ const merged = backfillAuthState({
5825
6950
  ...document.state,
5826
6951
  ...newState
5827
- };
6952
+ });
6953
+ merged.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, merged.auth);
6954
+ document.state = merged;
5828
6955
  document.initialState = document.state;
5829
6956
  }
5830
6957
  }
@@ -5884,7 +7011,6 @@ function computeUpgradeTransitions(manifest, fromVersion, toVersion) {
5884
7011
  }
5885
7012
  return transitions;
5886
7013
  }
5887
- const NON_DOMAIN_SCOPES$1 = new Set(["auth", "document"]);
5888
7014
  function highestReducerVersion(reducers) {
5889
7015
  const keys = Object.keys(reducers).map(Number);
5890
7016
  if (keys.length === 0) throw new Error("VersionedReplayConfig.reducers must not be empty");
@@ -5912,15 +7038,15 @@ function highestReducerVersion(reducers) {
5912
7038
  */
5913
7039
  function replayDocumentVersioned(initialState, operations, config, header, dispatch, options) {
5914
7040
  const { checkHashes = true, skipIndexValidation } = options || {};
5915
- const protocolVersion = header.protocolVersions?.["base-reducer"] ?? 1;
7041
+ const protocolVersion = baseReducerVersion(header);
5916
7042
  const spine = (operations["document"] ?? []).slice().sort((a, b) => a.index - b.index);
5917
7043
  const upgrades = spine.filter((op) => op.action.type === "UPGRADE_DOCUMENT");
5918
7044
  const legacyFallback = () => {
5919
- const domainOps = Object.fromEntries(Object.entries(operations).filter(([s]) => !NON_DOMAIN_SCOPES$1.has(s)));
7045
+ const replayOps = Object.fromEntries(Object.entries(operations).filter(([s]) => s !== "document"));
5920
7046
  const latestVersion = highestReducerVersion(config.reducers);
5921
7047
  const reducer = config.reducers[latestVersion];
5922
7048
  return {
5923
- ...replayDocument(initialState, domainOps, reducer, header, dispatch, {}, options),
7049
+ ...replayDocument(initialState, replayOps, reducer, header, dispatch, {}, options),
5924
7050
  operations
5925
7051
  };
5926
7052
  };
@@ -5944,14 +7070,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5944
7070
  const a = op.action;
5945
7071
  return a.input.fromVersion > 0 && a.input.fromVersion < a.input.toVersion;
5946
7072
  });
5947
- const domainScopes = Object.keys(operations).filter((s) => !NON_DOMAIN_SCOPES$1.has(s));
7073
+ const replayScopes = Object.keys(operations).filter((s) => s !== "document");
5948
7074
  const scopeOps = {};
5949
- for (const s of domainScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
7075
+ for (const s of replayScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
5950
7076
  const boundaries = validatedUpgrades.map((upgradeOp) => {
5951
7077
  const revisionSnapshot = upgradeOp.action.input.revision;
5952
7078
  const upgradeTimestamp = upgradeOp.timestampUtcMs;
5953
7079
  const boundary = {};
5954
- for (const s of domainScopes) {
7080
+ for (const s of replayScopes) {
5955
7081
  const ops = scopeOps[s] ?? [];
5956
7082
  if (revisionSnapshot !== void 0) {
5957
7083
  const rev = revisionSnapshot[s] ?? 0;
@@ -5969,7 +7095,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5969
7095
  }
5970
7096
  return boundary;
5971
7097
  });
5972
- for (let i = 1; i < boundaries.length; i++) for (const s of domainScopes) {
7098
+ for (let i = 1; i < boundaries.length; i++) for (const s of replayScopes) {
5973
7099
  const prev = boundaries[i - 1]?.[s] ?? 0;
5974
7100
  const curr = boundaries[i]?.[s] ?? 0;
5975
7101
  if (boundaries[i]) boundaries[i][s] = Math.max(prev, curr);
@@ -5981,10 +7107,11 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5981
7107
  ]);
5982
7108
  const initialOperations = {};
5983
7109
  for (const s of allScopes) initialOperations[s] = [];
7110
+ const backfilledSeed = backfillAuthState(seedState);
5984
7111
  let document = {
5985
7112
  header,
5986
- state: seedState,
5987
- initialState: seedState,
7113
+ state: backfilledSeed,
7114
+ initialState: backfilledSeed,
5988
7115
  operations: initialOperations,
5989
7116
  clipboard: []
5990
7117
  };
@@ -5992,17 +7119,15 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
5992
7119
  const segmentEndHashPerScope = /* @__PURE__ */ new Map();
5993
7120
  for (let k = 0; k <= validatedUpgrades.length; k++) {
5994
7121
  const reducer = config.reducers[currentVersion];
5995
- if (!reducer) {
5996
- const available = Object.keys(config.reducers).join(", ");
5997
- throw new Error(`No reducer registered for document version ${currentVersion}. Available versions: ${available}`);
5998
- }
5999
- for (const s of domainScopes) {
7122
+ if (!reducer) throw new UnsupportedDocumentModelVersionError(header.documentType, currentVersion, Object.keys(config.reducers).map(Number).sort((a, b) => a - b));
7123
+ for (const s of replayScopes) {
6000
7124
  const ops = scopeOps[s] ?? [];
6001
7125
  const segStart = k === 0 ? 0 : boundaries[k - 1]?.[s] ?? 0;
6002
7126
  const segEnd = k < validatedUpgrades.length ? boundaries[k]?.[s] ?? ops.length : ops.length;
6003
7127
  const segOps = ops.slice(segStart, segEnd);
6004
7128
  for (const op of segOps) {
6005
- document = reducer(document, op.action, dispatch, {
7129
+ if (isDenied(op)) document = updateHeaderRevision(appendWithoutApplying(document, op, s), s, op.timestampUtcMs);
7130
+ else document = reducer(document, op.action, dispatch, {
6006
7131
  ignoreSkipOperations: true,
6007
7132
  checkHashes,
6008
7133
  skipIndexValidation,
@@ -6019,6 +7144,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6019
7144
  if (!spineOp) continue;
6020
7145
  const spineActionType = spineOp.action.type;
6021
7146
  if (spineActionType === "CREATE_DOCUMENT" || spineActionType === "UPGRADE_DOCUMENT") continue;
7147
+ if (isDenied(spineOp)) continue;
6022
7148
  if (spineActionType === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, spineOp.action);
6023
7149
  else document = reducer(document, spineOp.action, dispatch, {
6024
7150
  ignoreSkipOperations: true,
@@ -6049,7 +7175,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6049
7175
  }
6050
7176
  };
6051
7177
  if (!checkHashes) {
6052
- const allReplayedOps = domainScopes.flatMap((s) => scopeOps[s] ?? []);
7178
+ const allReplayedOps = replayScopes.flatMap((s) => scopeOps[s] ?? []);
6053
7179
  for (const scope of Object.keys(document.state)) {
6054
7180
  const capturedHash = segmentEndHashPerScope.get(scope);
6055
7181
  const scopeHash = capturedHash !== void 0 ? capturedHash : hashDocumentStateForScope(document, scope);
@@ -6091,7 +7217,6 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
6091
7217
  }
6092
7218
  };
6093
7219
  }
6094
- const NON_DOMAIN_SCOPES = new Set(["auth", "document"]);
6095
7220
  function zipAsync(data) {
6096
7221
  return new Promise((resolve, reject) => {
6097
7222
  zip(data, (err, out) => err ? reject(err) : resolve(out));
@@ -6170,7 +7295,7 @@ async function parseZipData(data) {
6170
7295
  async function loadFromZipData(data, reducer, options) {
6171
7296
  const { initialState, header, clearedOperations } = await parseZipData(data);
6172
7297
  return {
6173
- ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => !NON_DOMAIN_SCOPES.has(scope))), reducer, header, void 0, {}, options),
7298
+ ...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => scope !== "document")), reducer, header, void 0, {}, options),
6174
7299
  operations: clearedOperations
6175
7300
  };
6176
7301
  }
@@ -6193,96 +7318,6 @@ const documentModelLoadFromInput = (input) => {
6193
7318
  const documentModelSaveToFileHandle = (document, input) => {
6194
7319
  return baseSaveToFileHandle(document, input);
6195
7320
  };
6196
- /**
6197
- * Creates a default PHAuthState
6198
- */
6199
- function defaultAuthState() {
6200
- return {};
6201
- }
6202
- /**
6203
- * Creates a default PHDocumentState
6204
- */
6205
- function defaultDocumentState() {
6206
- return {
6207
- version: 0,
6208
- hash: {
6209
- algorithm: HASH_ALGORITHM_SHA1,
6210
- encoding: HASH_ENCODING_BASE64
6211
- }
6212
- };
6213
- }
6214
- /**
6215
- * Creates a default PHBaseState with auth and document properties
6216
- */
6217
- function defaultBaseState() {
6218
- return {
6219
- auth: defaultAuthState(),
6220
- document: defaultDocumentState()
6221
- };
6222
- }
6223
- /**
6224
- * Creates a PHAuthState with the given properties
6225
- */
6226
- function createAuthState(auth) {
6227
- return {
6228
- ...defaultAuthState(),
6229
- ...auth
6230
- };
6231
- }
6232
- /**
6233
- * Creates a PHDocumentState with the given properties
6234
- */
6235
- function createDocumentState(document) {
6236
- return {
6237
- ...defaultDocumentState(),
6238
- ...document
6239
- };
6240
- }
6241
- /**
6242
- * Creates a PHBaseState with the given auth and document properties
6243
- */
6244
- function createBaseState(auth, document) {
6245
- return {
6246
- auth: createAuthState(auth),
6247
- document: createDocumentState(document)
6248
- };
6249
- }
6250
- function defaultGlobalState() {
6251
- return {
6252
- ...defaultBaseState(),
6253
- author: {
6254
- name: "",
6255
- website: ""
6256
- },
6257
- description: "",
6258
- extension: "",
6259
- id: "",
6260
- name: "",
6261
- specifications: []
6262
- };
6263
- }
6264
- function defaultLocalState() {
6265
- return {};
6266
- }
6267
- function createGlobalState(state) {
6268
- return {
6269
- ...defaultGlobalState(),
6270
- ...state || {}
6271
- };
6272
- }
6273
- function createLocalState(state) {
6274
- return {
6275
- ...defaultLocalState(),
6276
- ...state || {}
6277
- };
6278
- }
6279
- function createState(baseState, globalState, localState) {
6280
- return {
6281
- ...createBaseState(baseState?.auth, baseState?.document),
6282
- global: createGlobalState(globalState),
6283
- local: createLocalState(localState)
6284
- };
6285
- }
6286
7321
  //#endregion
6287
7322
  //#region ../document-model/dist/index.js
6288
7323
  const isStateOfType$1 = (state) => {
@@ -7361,6 +8396,6 @@ const driveDocumentModelModule = {
7361
8396
  }
7362
8397
  };
7363
8398
  //#endregion
7364
- 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 };
8399
+ export { setOperationErrorName as $, createZip as A, redo as B, baseSaveToFileHandle as C, createPresignedHeader as D, createBaseState as E, deserializeSignature as F, setInitialState as G, replayDocumentVersioned as H, documentModelDocumentType as I, setModelId as J, setModelDescription as K, hashDocumentStateForScope as L, deleteModule as M, deleteOperation as N, createReducer as O, deleteOperationError as P, setOperationDescription as Q, isDocumentAction as R, baseLoadFromInputVersioned as S, createAction as T, setAuthorName as U, releaseNewVersion as V, setAuthorWebsite as W, setModuleName as X, setModelName as Y, setName as Z, addOperationError as _, handleTargetNameCollisions as a, validateInitialState as at, baseCreateDocument as b, isValidName as c, generateId as ct, documentModelDocumentModelModule as d, setOperationName as et, logger as f, addOperation as g, addModule as h, generateNodesCopy as i, undo as it, defaultBaseState as j, createState as k, moveNode as l, BaseDocumentStateSchema as m, copyNode as n, setStateSchema as nt, isFileNode as o, validateModules as ot, BaseDocumentHeaderSchema as p, setModelExtension as q, driveDocumentModelModule as r, toTransportAction as rt, isFolderNode as s, validateStateSchemaName as st, addFolder as t, setOperationSchema as tt, updateNode as u, assertAuthPreservedOnDuplicate as v, buildSignedAction as w, baseLoadFromInput as x, baseActions as y, normalizeDocumentModelVersion as z };
7365
8400
 
7366
- //# sourceMappingURL=document-drive-IdFV1QXL.js.map
8401
+ //# sourceMappingURL=document-drive-cxHraTVQ.js.map