@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.
- package/dist/browser/assets/{entry-Cvoo4_c9.js → entry-D-JZwwGk.js} +3 -2
- package/dist/browser/assets/{projection-entry-DVec8YRh.js → projection-entry-64-WZZjU.js} +4 -4
- package/dist/browser/{linting-Dn6pXRkK.js → button-CuO8Gh3v.js} +20 -6
- package/dist/browser/button-CuO8Gh3v.js.map +1 -0
- package/dist/browser/{connect-KEU-HR3o.js → connect-D1KIMa_5.js} +916 -200
- package/dist/browser/connect-D1KIMa_5.js.map +1 -0
- package/dist/browser/{dist-CXoKspdx.js → dist-B4FWbuEM.js} +10 -2
- package/dist/browser/{dist-CXoKspdx.js.map → dist-B4FWbuEM.js.map} +1 -1
- package/dist/{node/dist-BeZaEP6S.mjs → browser/dist-BAJ5nRDl.js} +21 -2
- package/dist/browser/{dist-D8H48e8N.js.map → dist-BAJ5nRDl.js.map} +1 -1
- package/dist/browser/{dist-D5ra2aRj.js → dist-dmLcxsWV.js} +1588 -1293
- package/dist/browser/dist-dmLcxsWV.js.map +1 -0
- package/dist/browser/{document-drive-IdFV1QXL.js → document-drive-cxHraTVQ.js} +1157 -122
- package/dist/browser/document-drive-cxHraTVQ.js.map +1 -0
- package/dist/browser/document-models/index.js +1 -1
- package/dist/browser/{documents-D4ek7Ubv.js → documents-J_l59mSV.js} +5 -604
- package/dist/browser/documents-J_l59mSV.js.map +1 -0
- package/dist/browser/{editor-DiZF3ao3.js → editor-BAUTcFP2.js} +671 -236
- package/dist/browser/editor-BAUTcFP2.js.map +1 -0
- package/dist/browser/editor-PmQcB82h.js +25 -0
- package/dist/browser/editor-PmQcB82h.js.map +1 -0
- package/dist/browser/editors/document-model-editor/module.js +1 -1
- package/dist/browser/editors/generic-drive-explorer/index.js +5 -5
- package/dist/browser/editors/generic-drive-explorer/module.js +1 -1
- package/dist/{node/folder-view-Cy-QxsDg.mjs → browser/folder-view-CWnDdLMC.js} +73 -46
- package/dist/browser/folder-view-CWnDdLMC.js.map +1 -0
- package/dist/browser/{graphql-editor-ZLRO_8Ce.js → graphql-editor-Cq_L5IUQ.js} +8 -7
- package/dist/browser/{graphql-editor-ZLRO_8Ce.js.map → graphql-editor-Cq_L5IUQ.js.map} +1 -1
- package/dist/browser/{hooks-CORHBNHb.js → hooks-KKcNp2X0.js} +2 -2
- package/dist/browser/{hooks-CORHBNHb.js.map → hooks-KKcNp2X0.js.map} +1 -1
- package/dist/browser/index.js +378 -9
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/{json-editor-DBuZ6gN7.js → json-editor-D8lysNHE.js} +4 -4
- package/dist/browser/{json-editor-DBuZ6gN7.js.map → json-editor-D8lysNHE.js.map} +1 -1
- package/dist/browser/{lib-C-f3izXz.js → lib-DKvC6Hnv.js} +13 -4
- package/dist/browser/lib-DKvC6Hnv.js.map +1 -0
- package/dist/browser/printer-CEYr4JOg.js +604 -0
- package/dist/browser/printer-CEYr4JOg.js.map +1 -0
- package/dist/browser/{schema-context-DY-GEGnY.js → schema-context-ZyeclQwy.js} +2 -2
- package/dist/browser/{schema-context-DY-GEGnY.js.map → schema-context-ZyeclQwy.js.map} +1 -1
- package/dist/browser/{state-schemas-BjQKM498.js → state-schemas-NyThZN-C.js} +8 -18
- package/dist/browser/state-schemas-NyThZN-C.js.map +1 -0
- package/dist/node/{linting-Dx50GuLN.mjs → button-Da7CxMn7.mjs} +20 -7
- package/dist/node/button-Da7CxMn7.mjs.map +1 -0
- package/dist/node/{connect-KcYZcy_Z.mjs → connect-BixjoUPL.mjs} +916 -200
- package/dist/node/connect-BixjoUPL.mjs.map +1 -0
- package/dist/node/{dist-DJEK2WVi.mjs → dist-BtnA_6jV.mjs} +1592 -1292
- package/dist/node/dist-BtnA_6jV.mjs.map +1 -0
- package/dist/{browser/dist-D8H48e8N.js → node/dist-CzYOpeXN.mjs} +21 -2
- package/dist/node/{dist-BeZaEP6S.mjs.map → dist-CzYOpeXN.mjs.map} +1 -1
- package/dist/node/{dist-fA4EItr0.mjs → dist-DT7jI9ol.mjs} +10 -2
- package/dist/node/{dist-fA4EItr0.mjs.map → dist-DT7jI9ol.mjs.map} +1 -1
- package/dist/node/{document-drive-C8hbqQ3C.mjs → document-drive-ChLoGFvU.mjs} +1157 -122
- package/dist/node/document-drive-ChLoGFvU.mjs.map +1 -0
- package/dist/node/document-models/index.mjs +1 -1
- package/dist/node/{documents-BFBPmyw1.mjs → documents-DCf53bzO.mjs} +3 -3
- package/dist/node/{documents-BFBPmyw1.mjs.map → documents-DCf53bzO.mjs.map} +1 -1
- package/dist/node/editor-C8Drq-be.mjs +25 -0
- package/dist/node/editor-C8Drq-be.mjs.map +1 -0
- package/dist/node/{editor-B1KhoF1f.mjs → editor-cfoi-9LF.mjs} +672 -237
- package/dist/node/editor-cfoi-9LF.mjs.map +1 -0
- package/dist/node/editors/document-model-editor/module.mjs +1 -1
- package/dist/node/editors/generic-drive-explorer/index.mjs +5 -5
- package/dist/node/editors/generic-drive-explorer/module.mjs +1 -1
- package/dist/{browser/folder-view-DMFqcJhc.js → node/folder-view--I9GOsir.mjs} +73 -46
- package/dist/node/folder-view--I9GOsir.mjs.map +1 -0
- package/dist/node/{graphql-DskHmkMe.mjs → graphql-BfAoPeWM.mjs} +2 -2
- package/dist/node/{graphql-DskHmkMe.mjs.map → graphql-BfAoPeWM.mjs.map} +1 -1
- package/dist/node/{graphql-editor-C5VLRqEf.mjs → graphql-editor-BKGQ8ojN.mjs} +7 -7
- package/dist/node/{graphql-editor-C5VLRqEf.mjs.map → graphql-editor-BKGQ8ojN.mjs.map} +1 -1
- package/dist/node/{hooks-DJrjfVaS.mjs → hooks-D0DcS3MV.mjs} +2 -2
- package/dist/node/{hooks-DJrjfVaS.mjs.map → hooks-D0DcS3MV.mjs.map} +1 -1
- package/dist/node/index.mjs +378 -9
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/{json-editor-iI2s9a_4.mjs → json-editor-32paCJkU.mjs} +4 -4
- package/dist/node/{json-editor-iI2s9a_4.mjs.map → json-editor-32paCJkU.mjs.map} +1 -1
- package/dist/node/{main-D82dkD7g.mjs → main-DRfab-vL.mjs} +15 -6
- package/dist/node/main-DRfab-vL.mjs.map +1 -0
- package/dist/node/{schema-context-CX5ml_9t.mjs → schema-context-DL8jhi_d.mjs} +3 -3
- package/dist/node/{schema-context-CX5ml_9t.mjs.map → schema-context-DL8jhi_d.mjs.map} +1 -1
- package/dist/node/{state-schemas-Bl7nA8bB.mjs → state-schemas-ClrkM0Fn.mjs} +10 -20
- package/dist/{browser/state-schemas-BjQKM498.js.map → node/state-schemas-ClrkM0Fn.mjs.map} +1 -1
- package/package.json +8 -7
- package/dist/browser/connect-KEU-HR3o.js.map +0 -1
- package/dist/browser/dist-D5ra2aRj.js.map +0 -1
- package/dist/browser/document-drive-IdFV1QXL.js.map +0 -1
- package/dist/browser/documents-D4ek7Ubv.js.map +0 -1
- package/dist/browser/editor-DiZF3ao3.js.map +0 -1
- package/dist/browser/editor-SqNULDP-.js +0 -29
- package/dist/browser/editor-SqNULDP-.js.map +0 -1
- package/dist/browser/folder-view-DMFqcJhc.js.map +0 -1
- package/dist/browser/lib-C-f3izXz.js.map +0 -1
- package/dist/browser/linting-Dn6pXRkK.js.map +0 -1
- package/dist/node/connect-KcYZcy_Z.mjs.map +0 -1
- package/dist/node/dist-DJEK2WVi.mjs.map +0 -1
- package/dist/node/document-drive-C8hbqQ3C.mjs.map +0 -1
- package/dist/node/editor-B1KhoF1f.mjs.map +0 -1
- package/dist/node/editor-yBGEvMGD.mjs +0 -29
- package/dist/node/editor-yBGEvMGD.mjs.map +0 -1
- package/dist/node/folder-view-Cy-QxsDg.mjs.map +0 -1
- package/dist/node/linting-Dx50GuLN.mjs.map +0 -1
- package/dist/node/main-D82dkD7g.mjs.map +0 -1
- package/dist/node/state-schemas-Bl7nA8bB.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
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();
|
|
@@ -2963,6 +2997,76 @@ function castDraft(value) {
|
|
|
2963
2997
|
}
|
|
2964
2998
|
//#endregion
|
|
2965
2999
|
//#region ../shared/dist/document-model/index.js
|
|
3000
|
+
/**
|
|
3001
|
+
* What separates a signature's params when it travels as one string.
|
|
3002
|
+
*
|
|
3003
|
+
* GraphQL declares `signatures` as a list of strings, not a list of lists, so a
|
|
3004
|
+
* tuple is joined for transport and split on arrival. The separator is here so
|
|
3005
|
+
* the two halves cannot disagree about it - they live in different packages, and
|
|
3006
|
+
* a mismatch would corrupt every signature that crossed the wire rather than
|
|
3007
|
+
* failing outright.
|
|
3008
|
+
*/
|
|
3009
|
+
const SIGNATURE_PARAM_SEPARATOR = ", ";
|
|
3010
|
+
/** The number of params a signature carries. */
|
|
3011
|
+
const SIGNATURE_PARAM_COUNT = 5;
|
|
3012
|
+
/** Joins a signature's params for transport. Already-joined input passes through. */
|
|
3013
|
+
function serializeSignature(signature) {
|
|
3014
|
+
return Array.isArray(signature) ? signature.join(SIGNATURE_PARAM_SEPARATOR) : signature;
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Splits a transported signature back into its params. A tuple passes through.
|
|
3018
|
+
*
|
|
3019
|
+
* Short input is padded rather than refused: verification reads the params by
|
|
3020
|
+
* position and fails on a wrong one, which says more than a length complaint
|
|
3021
|
+
* raised here would.
|
|
3022
|
+
*/
|
|
3023
|
+
function deserializeSignature(signature) {
|
|
3024
|
+
if (Array.isArray(signature)) return signature;
|
|
3025
|
+
const parts = signature.split(SIGNATURE_PARAM_SEPARATOR);
|
|
3026
|
+
return Array.from({ length: SIGNATURE_PARAM_COUNT }, (_unused, index) => parts[index] ?? "");
|
|
3027
|
+
}
|
|
3028
|
+
/**
|
|
3029
|
+
* Projects an action onto the fields the GraphQL `ActionInput` declares.
|
|
3030
|
+
*
|
|
3031
|
+
* A projection rather than a spread, because an input object rejects a field it
|
|
3032
|
+
* does not declare, and that refusal takes the whole request with it. An action
|
|
3033
|
+
* read back out of storage can carry fields the type no longer has - a legacy
|
|
3034
|
+
* `attachments` array, or the operation fields left behind by a signing helper
|
|
3035
|
+
* that returns an operation - and any one of them would sink an otherwise valid
|
|
3036
|
+
* submission.
|
|
3037
|
+
*
|
|
3038
|
+
* Only fields the action actually carries are emitted, so an unsigned action
|
|
3039
|
+
* sends no context at all rather than a context full of nulls.
|
|
3040
|
+
*/
|
|
3041
|
+
function toTransportAction(action) {
|
|
3042
|
+
if (action.input === void 0 || action.input === null) throw new Error(`Action ${action.id} (${action.type}) has no input, which the wire requires`);
|
|
3043
|
+
const projected = {
|
|
3044
|
+
id: action.id,
|
|
3045
|
+
type: action.type,
|
|
3046
|
+
timestampUtcMs: action.timestampUtcMs,
|
|
3047
|
+
input: action.input,
|
|
3048
|
+
scope: action.scope
|
|
3049
|
+
};
|
|
3050
|
+
const context = toTransportContext(action.context);
|
|
3051
|
+
return context ? {
|
|
3052
|
+
...projected,
|
|
3053
|
+
context
|
|
3054
|
+
} : projected;
|
|
3055
|
+
}
|
|
3056
|
+
function toTransportContext(context) {
|
|
3057
|
+
if (!context) return;
|
|
3058
|
+
const projected = {};
|
|
3059
|
+
if (context.prevOpIndex !== void 0) projected.prevOpIndex = context.prevOpIndex;
|
|
3060
|
+
if (context.prevOpHash !== void 0) projected.prevOpHash = context.prevOpHash;
|
|
3061
|
+
if (context.nonce !== void 0) projected.nonce = context.nonce;
|
|
3062
|
+
const signer = context.signer;
|
|
3063
|
+
if (signer) projected.signer = {
|
|
3064
|
+
...signer.user ? { user: signer.user } : {},
|
|
3065
|
+
...signer.app ? { app: signer.app } : {},
|
|
3066
|
+
signatures: (signer.signatures ?? []).map(serializeSignature)
|
|
3067
|
+
};
|
|
3068
|
+
return Object.keys(projected).length > 0 ? projected : void 0;
|
|
3069
|
+
}
|
|
2966
3070
|
var InvalidActionInputError = class extends Error {
|
|
2967
3071
|
data;
|
|
2968
3072
|
constructor(data) {
|
|
@@ -2995,6 +3099,75 @@ var DowngradeNotSupportedError = class extends Error {
|
|
|
2995
3099
|
this.toVersion = toVersion;
|
|
2996
3100
|
}
|
|
2997
3101
|
};
|
|
3102
|
+
/**
|
|
3103
|
+
* Thrown when INITIALIZE_AUTH is applied to an already-initialized auth scope.
|
|
3104
|
+
* The genesis action is valid only at auth revision zero.
|
|
3105
|
+
*/
|
|
3106
|
+
var AuthAlreadyInitializedError = class extends Error {
|
|
3107
|
+
documentId;
|
|
3108
|
+
constructor(documentId) {
|
|
3109
|
+
super(`Auth scope already initialized for document ${documentId}: INITIALIZE_AUTH is valid only at auth revision zero`);
|
|
3110
|
+
this.name = "AuthAlreadyInitializedError";
|
|
3111
|
+
this.documentId = documentId;
|
|
3112
|
+
}
|
|
3113
|
+
};
|
|
3114
|
+
/**
|
|
3115
|
+
* Thrown when INITIALIZE_AUTH is not signed by the document creator (its signer
|
|
3116
|
+
* does not match `header.sig.publicKey`), so it cannot set the auth policy.
|
|
3117
|
+
*/
|
|
3118
|
+
var AuthInitializerNotCreatorError = class extends Error {
|
|
3119
|
+
documentId;
|
|
3120
|
+
constructor(documentId) {
|
|
3121
|
+
super(`INITIALIZE_AUTH for document ${documentId} must be signed by the document creator`);
|
|
3122
|
+
this.name = "AuthInitializerNotCreatorError";
|
|
3123
|
+
this.documentId = documentId;
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
/**
|
|
3127
|
+
* Thrown when INITIALIZE_AUTH carries a version below 1. Version 0 is reserved
|
|
3128
|
+
* for the uninitialized auth scope.
|
|
3129
|
+
*/
|
|
3130
|
+
var InvalidAuthVersionError = class extends Error {
|
|
3131
|
+
documentId;
|
|
3132
|
+
version;
|
|
3133
|
+
constructor(documentId, version) {
|
|
3134
|
+
super(`Invalid auth policy version ${version} for document ${documentId}: INITIALIZE_AUTH requires an integer version >= 1`);
|
|
3135
|
+
this.name = "InvalidAuthVersionError";
|
|
3136
|
+
this.documentId = documentId;
|
|
3137
|
+
this.version = version;
|
|
3138
|
+
}
|
|
3139
|
+
};
|
|
3140
|
+
/** Thrown when a duplicate would lose the source policy's version or creator binding. */
|
|
3141
|
+
var AuthPolicyNotPreservedError = class extends Error {
|
|
3142
|
+
documentId;
|
|
3143
|
+
constructor(documentId) {
|
|
3144
|
+
super(`Duplicating document ${documentId} would not preserve its auth policy: the copy loses the policy version or its creator binding`);
|
|
3145
|
+
this.name = "AuthPolicyNotPreservedError";
|
|
3146
|
+
this.documentId = documentId;
|
|
3147
|
+
}
|
|
3148
|
+
};
|
|
3149
|
+
/**
|
|
3150
|
+
* Thrown when a grant referenced by REMOVE_GRANT or MOVE_GRANT does not exist.
|
|
3151
|
+
*/
|
|
3152
|
+
var GrantNotFoundError = class extends Error {
|
|
3153
|
+
grantId;
|
|
3154
|
+
constructor(grantId) {
|
|
3155
|
+
super(`Grant not found in auth scope: ${grantId}`);
|
|
3156
|
+
this.name = "GrantNotFoundError";
|
|
3157
|
+
this.grantId = grantId;
|
|
3158
|
+
}
|
|
3159
|
+
};
|
|
3160
|
+
/**
|
|
3161
|
+
* Thrown when a disallowed action (UNDO, REDO, PRUNE) targets the auth scope.
|
|
3162
|
+
*/
|
|
3163
|
+
var AuthActionNotAllowedError = class extends Error {
|
|
3164
|
+
actionType;
|
|
3165
|
+
constructor(actionType) {
|
|
3166
|
+
super(`${actionType} is not permitted on the auth scope`);
|
|
3167
|
+
this.name = "AuthActionNotAllowedError";
|
|
3168
|
+
this.actionType = actionType;
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
2998
3171
|
var HashMismatchError = class extends Error {
|
|
2999
3172
|
_scope;
|
|
3000
3173
|
_document;
|
|
@@ -3021,6 +3194,25 @@ var HashMismatchError = class extends Error {
|
|
|
3021
3194
|
return this._operation;
|
|
3022
3195
|
}
|
|
3023
3196
|
};
|
|
3197
|
+
/**
|
|
3198
|
+
* Thrown when replay or import requires a document model version that is not
|
|
3199
|
+
* registered. Carries the data the UI needs to explain the mismatch.
|
|
3200
|
+
*/
|
|
3201
|
+
var UnsupportedDocumentModelVersionError = class extends Error {
|
|
3202
|
+
documentType;
|
|
3203
|
+
requiredVersion;
|
|
3204
|
+
availableVersions;
|
|
3205
|
+
constructor(documentType, requiredVersion, availableVersions) {
|
|
3206
|
+
super(`No reducer registered for document version ${requiredVersion}. Available versions: ${availableVersions.join(", ")}`);
|
|
3207
|
+
this.name = "UnsupportedDocumentModelVersionError";
|
|
3208
|
+
this.documentType = documentType;
|
|
3209
|
+
this.requiredVersion = requiredVersion;
|
|
3210
|
+
this.availableVersions = availableVersions;
|
|
3211
|
+
}
|
|
3212
|
+
static isError(error) {
|
|
3213
|
+
return Error.isError(error) && error.name === "UnsupportedDocumentModelVersionError";
|
|
3214
|
+
}
|
|
3215
|
+
};
|
|
3024
3216
|
const isDefinedNonNullAny$1 = (v) => v !== void 0 && v !== null;
|
|
3025
3217
|
any().refine((v) => isDefinedNonNullAny$1(v));
|
|
3026
3218
|
const Load_StateSchema = _enum(["LOAD_STATE"]);
|
|
@@ -3461,6 +3653,88 @@ const ConfigEntrySchema = object({
|
|
|
3461
3653
|
required: boolean().optional(),
|
|
3462
3654
|
default: boolean().optional()
|
|
3463
3655
|
});
|
|
3656
|
+
const PwaUrlPatternSchema = union([string(), strictObject({
|
|
3657
|
+
source: string(),
|
|
3658
|
+
flags: string().optional()
|
|
3659
|
+
}).refine((p) => {
|
|
3660
|
+
try {
|
|
3661
|
+
new RegExp(p.source, p.flags);
|
|
3662
|
+
return true;
|
|
3663
|
+
} catch {
|
|
3664
|
+
return false;
|
|
3665
|
+
}
|
|
3666
|
+
}, { message: "source/flags do not compile to a valid RegExp" })]);
|
|
3667
|
+
const PwaRuntimeCachingSchema = strictObject({
|
|
3668
|
+
urlPattern: PwaUrlPatternSchema,
|
|
3669
|
+
handler: _enum([
|
|
3670
|
+
"CacheFirst",
|
|
3671
|
+
"CacheOnly",
|
|
3672
|
+
"NetworkFirst",
|
|
3673
|
+
"NetworkOnly",
|
|
3674
|
+
"StaleWhileRevalidate"
|
|
3675
|
+
]),
|
|
3676
|
+
method: _enum([
|
|
3677
|
+
"GET",
|
|
3678
|
+
"POST",
|
|
3679
|
+
"PUT",
|
|
3680
|
+
"DELETE",
|
|
3681
|
+
"HEAD",
|
|
3682
|
+
"PATCH"
|
|
3683
|
+
]).optional(),
|
|
3684
|
+
options: strictObject({
|
|
3685
|
+
cacheName: string().optional(),
|
|
3686
|
+
networkTimeoutSeconds: number().optional(),
|
|
3687
|
+
expiration: strictObject({
|
|
3688
|
+
maxEntries: number().optional(),
|
|
3689
|
+
maxAgeSeconds: number().optional()
|
|
3690
|
+
}).optional(),
|
|
3691
|
+
cacheableResponse: strictObject({
|
|
3692
|
+
statuses: array(number()).optional(),
|
|
3693
|
+
headers: record(string(), string()).optional()
|
|
3694
|
+
}).optional()
|
|
3695
|
+
}).optional()
|
|
3696
|
+
});
|
|
3697
|
+
const PwaIconSchema = strictObject({
|
|
3698
|
+
src: string(),
|
|
3699
|
+
sizes: string().optional(),
|
|
3700
|
+
type: string().optional(),
|
|
3701
|
+
purpose: string().optional()
|
|
3702
|
+
});
|
|
3703
|
+
const PwaFileHandlerSchema = strictObject({
|
|
3704
|
+
accept: record(string(), array(string().regex(/^\./, "file extensions must start with '.'"))),
|
|
3705
|
+
icons: array(PwaIconSchema).optional(),
|
|
3706
|
+
launch_type: _enum(["single-client", "multiple-clients"]).optional()
|
|
3707
|
+
});
|
|
3708
|
+
const PwaConfigSchema = strictObject({
|
|
3709
|
+
manifest: strictObject({
|
|
3710
|
+
name: string().optional(),
|
|
3711
|
+
short_name: string().optional(),
|
|
3712
|
+
description: string().optional(),
|
|
3713
|
+
theme_color: string().optional(),
|
|
3714
|
+
background_color: string().optional(),
|
|
3715
|
+
display: _enum([
|
|
3716
|
+
"fullscreen",
|
|
3717
|
+
"standalone",
|
|
3718
|
+
"minimal-ui",
|
|
3719
|
+
"browser"
|
|
3720
|
+
]).optional(),
|
|
3721
|
+
start_url: string().optional(),
|
|
3722
|
+
scope: string().optional(),
|
|
3723
|
+
icons: array(PwaIconSchema).optional(),
|
|
3724
|
+
file_handlers: array(PwaFileHandlerSchema).optional(),
|
|
3725
|
+
launch_handler: strictObject({ client_mode: _enum([
|
|
3726
|
+
"auto",
|
|
3727
|
+
"focus-existing",
|
|
3728
|
+
"navigate-existing",
|
|
3729
|
+
"navigate-new"
|
|
3730
|
+
]) }).optional()
|
|
3731
|
+
}).optional(),
|
|
3732
|
+
globPatterns: array(string()).optional(),
|
|
3733
|
+
globIgnores: array(string()).optional(),
|
|
3734
|
+
maximumFileSizeToCacheInBytes: number().optional(),
|
|
3735
|
+
runtimeCaching: array(PwaRuntimeCachingSchema).optional(),
|
|
3736
|
+
navigateFallbackDenylist: array(PwaUrlPatternSchema).optional()
|
|
3737
|
+
});
|
|
3464
3738
|
object({
|
|
3465
3739
|
name: string(),
|
|
3466
3740
|
description: string().optional(),
|
|
@@ -3472,7 +3746,8 @@ object({
|
|
|
3472
3746
|
editors: PowerhouseModulesSchema,
|
|
3473
3747
|
processors: PowerhouseModulesSchema,
|
|
3474
3748
|
subgraphs: PowerhouseModulesSchema,
|
|
3475
|
-
config: array(ConfigEntrySchema).optional()
|
|
3749
|
+
config: array(ConfigEntrySchema).optional(),
|
|
3750
|
+
pwa: PwaConfigSchema.optional()
|
|
3476
3751
|
});
|
|
3477
3752
|
/**
|
|
3478
3753
|
* Cancels the last `count` operations.
|
|
@@ -3743,6 +4018,505 @@ const actions = {
|
|
|
3743
4018
|
...baseActions,
|
|
3744
4019
|
...documentModelActions
|
|
3745
4020
|
};
|
|
4021
|
+
const documentModelDocumentType = "powerhouse/document-model";
|
|
4022
|
+
/**
|
|
4023
|
+
* Thrown when a grant violates the v1 validation rules. The message is stored
|
|
4024
|
+
* on error operations, so it must be a pure function of the input.
|
|
4025
|
+
*/
|
|
4026
|
+
var InvalidGrantError = class extends Error {
|
|
4027
|
+
grantId;
|
|
4028
|
+
constructor(grantId, problem) {
|
|
4029
|
+
super(`Invalid grant "${grantId}": ${problem}`);
|
|
4030
|
+
this.name = "InvalidGrantError";
|
|
4031
|
+
this.grantId = grantId;
|
|
4032
|
+
}
|
|
4033
|
+
};
|
|
4034
|
+
/** Thrown for a `{ group }` principal on a group document: references never chain. */
|
|
4035
|
+
var GroupPrincipalNotAllowedError = class extends Error {
|
|
4036
|
+
grantId;
|
|
4037
|
+
constructor(grantId) {
|
|
4038
|
+
super(`Grant "${grantId}" uses a group principal on a group document: a group's auth scope cannot reference other groups`);
|
|
4039
|
+
this.name = "GroupPrincipalNotAllowedError";
|
|
4040
|
+
this.grantId = grantId;
|
|
4041
|
+
}
|
|
4042
|
+
};
|
|
4043
|
+
/**
|
|
4044
|
+
* Thrown when a change would leave a creator-less policy with no grant
|
|
4045
|
+
* permitting execute on the auth scope. Without the creator carve-out no
|
|
4046
|
+
* subject could ever administer such a policy again.
|
|
4047
|
+
*/
|
|
4048
|
+
var AuthAdministrationLockoutError = class extends Error {
|
|
4049
|
+
grantId;
|
|
4050
|
+
constructor(grantId) {
|
|
4051
|
+
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`);
|
|
4052
|
+
this.name = "AuthAdministrationLockoutError";
|
|
4053
|
+
this.grantId = grantId;
|
|
4054
|
+
}
|
|
4055
|
+
};
|
|
4056
|
+
/**
|
|
4057
|
+
* Thrown when INITIALIZE_AUTH would create a creator-less policy with no grant
|
|
4058
|
+
* permitting execute on the auth scope. Such a policy would be born with no
|
|
4059
|
+
* subject able to administer it.
|
|
4060
|
+
*/
|
|
4061
|
+
var AuthAdministrationMissingError = class extends Error {
|
|
4062
|
+
constructor() {
|
|
4063
|
+
super("Initial grants include no reachable grant permitting execute on the auth scope: a policy with no creator must always include one");
|
|
4064
|
+
this.name = "AuthAdministrationMissingError";
|
|
4065
|
+
}
|
|
4066
|
+
};
|
|
4067
|
+
const GRANT_KEYS = new Set([
|
|
4068
|
+
"id",
|
|
4069
|
+
"description",
|
|
4070
|
+
"effect",
|
|
4071
|
+
"principal",
|
|
4072
|
+
"capability",
|
|
4073
|
+
"where"
|
|
4074
|
+
]);
|
|
4075
|
+
const PRINCIPAL_KINDS = new Set([
|
|
4076
|
+
"anyone",
|
|
4077
|
+
"address",
|
|
4078
|
+
"group",
|
|
4079
|
+
"match"
|
|
4080
|
+
]);
|
|
4081
|
+
const COMPARISON_CONDITION_KINDS = new Set([
|
|
4082
|
+
"eq",
|
|
4083
|
+
"ne",
|
|
4084
|
+
"lt",
|
|
4085
|
+
"lte",
|
|
4086
|
+
"gt",
|
|
4087
|
+
"gte"
|
|
4088
|
+
]);
|
|
4089
|
+
function isPlainValue(value) {
|
|
4090
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4091
|
+
}
|
|
4092
|
+
function operandProblem(value, capabilityScope, budget) {
|
|
4093
|
+
budget.nodes -= 1;
|
|
4094
|
+
if (budget.nodes < 0) return `condition exceeds 100 nodes`;
|
|
4095
|
+
if (!isPlainValue(value)) return "operand must be an object";
|
|
4096
|
+
const keys = Object.keys(value);
|
|
4097
|
+
if (keys.length !== 1) return "operand must have exactly one of attr or lit";
|
|
4098
|
+
if (keys[0] === "attr") {
|
|
4099
|
+
const attr = value.attr;
|
|
4100
|
+
if (typeof attr !== "string" || attr.length === 0) return "attr must be a non-empty string";
|
|
4101
|
+
if (capabilityScope !== void 0 && capabilityScope !== "*" && attr.startsWith("doc.")) {
|
|
4102
|
+
const pathScope = attr.split(".")[1] ?? "";
|
|
4103
|
+
if (pathScope !== capabilityScope) return `condition path "${attr}" reads scope "${pathScope}" but the capability covers only scope "${capabilityScope}"`;
|
|
4104
|
+
}
|
|
4105
|
+
return null;
|
|
4106
|
+
}
|
|
4107
|
+
if (keys[0] === "lit") {
|
|
4108
|
+
const lit = value.lit;
|
|
4109
|
+
if (lit !== null && typeof lit !== "string" && typeof lit !== "number" && typeof lit !== "boolean") return "lit must be a string, number, boolean, or null";
|
|
4110
|
+
if (typeof lit === "number" && !Number.isFinite(lit)) return "lit must be a finite number";
|
|
4111
|
+
if (typeof lit === "number" && Object.is(lit, -0)) return "lit must not be negative zero";
|
|
4112
|
+
return null;
|
|
4113
|
+
}
|
|
4114
|
+
return `unknown operand kind "${keys[0]}"`;
|
|
4115
|
+
}
|
|
4116
|
+
function conditionProblem(value, capabilityScope, depth, budget) {
|
|
4117
|
+
if (depth > 10) return `condition exceeds depth 10`;
|
|
4118
|
+
budget.nodes -= 1;
|
|
4119
|
+
if (budget.nodes < 0) return `condition exceeds 100 nodes`;
|
|
4120
|
+
if (!isPlainValue(value)) return "condition must be an object";
|
|
4121
|
+
const keys = Object.keys(value);
|
|
4122
|
+
if (keys.length !== 1) return "condition must have exactly one operator";
|
|
4123
|
+
const kind = keys[0];
|
|
4124
|
+
const body = value[kind];
|
|
4125
|
+
if (COMPARISON_CONDITION_KINDS.has(kind)) {
|
|
4126
|
+
if (!Array.isArray(body) || body.length !== 2) return `${kind} requires a pair of operands`;
|
|
4127
|
+
for (const operand of body) {
|
|
4128
|
+
const problem = operandProblem(operand, capabilityScope, budget);
|
|
4129
|
+
if (problem !== null) return problem;
|
|
4130
|
+
}
|
|
4131
|
+
return null;
|
|
4132
|
+
}
|
|
4133
|
+
if (kind === "in" || kind === "notIn") {
|
|
4134
|
+
if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return `${kind} requires an operand and an operand list`;
|
|
4135
|
+
const first = operandProblem(body[0], capabilityScope, budget);
|
|
4136
|
+
if (first !== null) return first;
|
|
4137
|
+
for (const operand of body[1]) {
|
|
4138
|
+
const problem = operandProblem(operand, capabilityScope, budget);
|
|
4139
|
+
if (problem !== null) return problem;
|
|
4140
|
+
}
|
|
4141
|
+
return null;
|
|
4142
|
+
}
|
|
4143
|
+
if (kind === "exists") return operandProblem(body, capabilityScope, budget);
|
|
4144
|
+
if (kind === "and" || kind === "or") {
|
|
4145
|
+
if (!Array.isArray(body)) return `${kind} requires a condition list`;
|
|
4146
|
+
for (const child of body) {
|
|
4147
|
+
const problem = conditionProblem(child, capabilityScope, depth + 1, budget);
|
|
4148
|
+
if (problem !== null) return problem;
|
|
4149
|
+
}
|
|
4150
|
+
return null;
|
|
4151
|
+
}
|
|
4152
|
+
if (kind === "not") return conditionProblem(body, capabilityScope, depth + 1, budget);
|
|
4153
|
+
return `unknown condition operator "${kind}"`;
|
|
4154
|
+
}
|
|
4155
|
+
function principalProblem(value, capabilityScope) {
|
|
4156
|
+
if (!isPlainValue(value)) return "principal must be an object";
|
|
4157
|
+
const keys = Object.keys(value);
|
|
4158
|
+
if (keys.length !== 1 || !PRINCIPAL_KINDS.has(keys[0])) return "principal must have exactly one of anyone, address, group, or match";
|
|
4159
|
+
const kind = keys[0];
|
|
4160
|
+
if (kind === "anyone" && value.anyone !== true) return "anyone must be true";
|
|
4161
|
+
if (kind === "address") {
|
|
4162
|
+
const address = value.address;
|
|
4163
|
+
if (typeof address !== "string" || address.length === 0) return "address must be a non-empty string";
|
|
4164
|
+
}
|
|
4165
|
+
if (kind === "group") {
|
|
4166
|
+
const group = value.group;
|
|
4167
|
+
if (typeof group !== "string" || group.length === 0) return "group must be a non-empty string";
|
|
4168
|
+
}
|
|
4169
|
+
if (kind === "match") return conditionProblem(value.match, capabilityScope, 1, { nodes: 100 });
|
|
4170
|
+
return null;
|
|
4171
|
+
}
|
|
4172
|
+
function capabilityProblem(value) {
|
|
4173
|
+
if (!isPlainValue(value)) return "capability must be an object";
|
|
4174
|
+
const can = value.can;
|
|
4175
|
+
if (can !== "read" && can !== "execute") return "capability.can must be read or execute";
|
|
4176
|
+
const allowedKeys = can === "execute" ? [
|
|
4177
|
+
"can",
|
|
4178
|
+
"scope",
|
|
4179
|
+
"operation"
|
|
4180
|
+
] : ["can", "scope"];
|
|
4181
|
+
const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key)).sort();
|
|
4182
|
+
if (unknownKeys.length > 0) return `unknown capability key "${unknownKeys[0]}"`;
|
|
4183
|
+
if (value.scope !== void 0) {
|
|
4184
|
+
if (typeof value.scope !== "string" || value.scope.length === 0) return "capability.scope must be a non-empty string";
|
|
4185
|
+
}
|
|
4186
|
+
if (can === "execute" && value.operation !== void 0) {
|
|
4187
|
+
const operation = value.operation;
|
|
4188
|
+
if (!Array.isArray(operation)) return "capability.operation must be an array";
|
|
4189
|
+
if (operation.length > 100) return `capability.operation exceeds 100 entries`;
|
|
4190
|
+
for (const entry of operation) if (typeof entry !== "string" || entry.length === 0) return "capability.operation entries must be non-empty strings";
|
|
4191
|
+
}
|
|
4192
|
+
return null;
|
|
4193
|
+
}
|
|
4194
|
+
/** Returns the first v1-rule violation, or null. Pure, total, deterministic. */
|
|
4195
|
+
function grantProblem(value) {
|
|
4196
|
+
if (!isPlainValue(value)) return "grant must be an object";
|
|
4197
|
+
const unknownKeys = Object.keys(value).filter((key) => !GRANT_KEYS.has(key)).sort();
|
|
4198
|
+
if (unknownKeys.length > 0) return `unknown grant key "${unknownKeys[0]}"`;
|
|
4199
|
+
if (typeof value.id !== "string" || value.id.length === 0) return "id must be a non-empty string";
|
|
4200
|
+
if (typeof value.description !== "string") return "description must be a string";
|
|
4201
|
+
if (value.effect !== "allow" && value.effect !== "deny") return "effect must be allow or deny";
|
|
4202
|
+
const capabilityValue = value.capability;
|
|
4203
|
+
const capability = capabilityProblem(capabilityValue);
|
|
4204
|
+
if (capability !== null) return capability;
|
|
4205
|
+
const capabilityScope = capabilityValue.scope;
|
|
4206
|
+
const principal = principalProblem(value.principal, capabilityScope);
|
|
4207
|
+
if (principal !== null) return principal;
|
|
4208
|
+
if (value.where !== void 0) return conditionProblem(value.where, capabilityScope, 1, { nodes: 100 });
|
|
4209
|
+
return null;
|
|
4210
|
+
}
|
|
4211
|
+
/** V1 shape rules plus the group-document group-principal ban. */
|
|
4212
|
+
function assertValidGrant(grant, documentType) {
|
|
4213
|
+
const grantId = isPlainValue(grant) && typeof grant.id === "string" ? grant.id : "";
|
|
4214
|
+
const problem = grantProblem(grant);
|
|
4215
|
+
if (problem !== null) throw new InvalidGrantError(grantId, problem);
|
|
4216
|
+
if (documentType === "powerhouse/reactor-group" && "group" in grant.principal) throw new GroupPrincipalNotAllowedError(grantId);
|
|
4217
|
+
}
|
|
4218
|
+
/**
|
|
4219
|
+
* Validates an initial grant list: the count cap, every grant, and — on a
|
|
4220
|
+
* creator-less policy — that some grant keeps the auth scope administrable.
|
|
4221
|
+
*/
|
|
4222
|
+
function assertValidInitialGrants(grants, documentType, creator) {
|
|
4223
|
+
if (grants.length > 100) throw new InvalidGrantError("", `policy exceeds 100 grants`);
|
|
4224
|
+
for (const grant of grants) assertValidGrant(grant, documentType);
|
|
4225
|
+
if (creator === void 0 && !administrationReachable(grants)) throw new AuthAdministrationMissingError();
|
|
4226
|
+
}
|
|
4227
|
+
/**
|
|
4228
|
+
* Validates a grant upsert: the grant itself, the count cap on append, and
|
|
4229
|
+
* administration retention. Retention is checked on an append as well as an
|
|
4230
|
+
* in-place replace, because a grant appended after the administration grant can
|
|
4231
|
+
* shadow it (evaluation is last-applicable-grant-wins) and so take
|
|
4232
|
+
* administration away without removing anything.
|
|
4233
|
+
*/
|
|
4234
|
+
function assertValidGrantUpsert(grant, existing, documentType, creator) {
|
|
4235
|
+
assertValidGrant(grant, documentType);
|
|
4236
|
+
const exists = existing.some((g) => g.id === grant.id);
|
|
4237
|
+
if (!exists && existing.length >= 100) throw new InvalidGrantError(grant.id, `policy exceeds 100 grants`);
|
|
4238
|
+
assertAuthAdministrationRetained(creator, existing, exists ? existing.map((g) => g.id === grant.id ? grant : g) : [...existing, grant], grant.id);
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* The request whose coverage keeps a policy administrable: a subject who may
|
|
4242
|
+
* SET_GRANT can upsert any grant, so every other repair stays reachable.
|
|
4243
|
+
*/
|
|
4244
|
+
const AUTH_ADMINISTRATION_REQUEST = {
|
|
4245
|
+
verb: "execute",
|
|
4246
|
+
scope: "auth",
|
|
4247
|
+
operation: "SET_GRANT"
|
|
4248
|
+
};
|
|
4249
|
+
/**
|
|
4250
|
+
* Whether some subject can still administer the auth scope under this grant list.
|
|
4251
|
+
*
|
|
4252
|
+
* Asks the evaluator rather than pattern-matching a single grant: evaluation is
|
|
4253
|
+
* last-applicable-grant-wins, so an allow that some later deny shadows keeps
|
|
4254
|
+
* nothing reachable even though it is still present in the list. Only anyone and
|
|
4255
|
+
* address principals are candidates, because those are the ones v1 can match at
|
|
4256
|
+
* all; a `where` condition or a group or match principal never applies.
|
|
4257
|
+
*/
|
|
4258
|
+
function administrationReachable(grants) {
|
|
4259
|
+
return grants.some((grant) => {
|
|
4260
|
+
const subject = administrationCandidate(grant);
|
|
4261
|
+
if (subject === void 0) return false;
|
|
4262
|
+
return evaluateGrantStack(grants, subject, AUTH_ADMINISTRATION_REQUEST).decision === "allow";
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
/**
|
|
4266
|
+
* The subject to test this grant with, or undefined when the grant could never
|
|
4267
|
+
* carry administration for anyone.
|
|
4268
|
+
*/
|
|
4269
|
+
function administrationCandidate(grant) {
|
|
4270
|
+
if (grant.effect !== "allow" || grant.where !== void 0 || !capabilityCovers(grant.capability, AUTH_ADMINISTRATION_REQUEST)) return;
|
|
4271
|
+
if ("address" in grant.principal) return {
|
|
4272
|
+
address: grant.principal.address,
|
|
4273
|
+
key: void 0
|
|
4274
|
+
};
|
|
4275
|
+
if ("anyone" in grant.principal) return {
|
|
4276
|
+
address: void 0,
|
|
4277
|
+
key: void 0
|
|
4278
|
+
};
|
|
4279
|
+
}
|
|
4280
|
+
/**
|
|
4281
|
+
* A creator-less policy must always retain a grant permitting execute on the
|
|
4282
|
+
* auth scope; on a signed document the creator carve-out keeps administration
|
|
4283
|
+
* reachable instead. Rejects a change that takes the last such grant away. A
|
|
4284
|
+
* policy already without one is left alone: the change is not what locks it.
|
|
4285
|
+
*/
|
|
4286
|
+
function assertAuthAdministrationRetained(creator, previous, next, grantId) {
|
|
4287
|
+
if (creator !== void 0) return;
|
|
4288
|
+
if (administrationReachable(previous) && !administrationReachable(next)) throw new AuthAdministrationLockoutError(grantId);
|
|
4289
|
+
}
|
|
4290
|
+
/**
|
|
4291
|
+
* An operand whose shape validation would have rejected. Distinguished from
|
|
4292
|
+
* an unresolved path so a structurally malformed operand poisons its whole
|
|
4293
|
+
* condition to false rather than reading as "absent", which `not` would
|
|
4294
|
+
* otherwise widen to true.
|
|
4295
|
+
*/
|
|
4296
|
+
const INVALID_OPERAND = Symbol("invalid-operand");
|
|
4297
|
+
/**
|
|
4298
|
+
* Narrows to the values conditions compare. An object, array, or non-finite
|
|
4299
|
+
* number resolves to undefined, and every comparison involving undefined is
|
|
4300
|
+
* false.
|
|
4301
|
+
*/
|
|
4302
|
+
function asConditionValue(value) {
|
|
4303
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
4304
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
4305
|
+
}
|
|
4306
|
+
/**
|
|
4307
|
+
* Resolves one operand. Attr roots: `subject.*`, `doc.<scope>.*` where the
|
|
4308
|
+
* scope must be the executing scope (validation already rejects any other,
|
|
4309
|
+
* but resolution stays total), and `action.input.*`. Path steps read own
|
|
4310
|
+
* properties only, so prototype members can never influence a verdict.
|
|
4311
|
+
*/
|
|
4312
|
+
function resolveOperand(operand, subject, request, conditions) {
|
|
4313
|
+
if (!isPlainValue(operand)) return INVALID_OPERAND;
|
|
4314
|
+
const keys = Object.keys(operand);
|
|
4315
|
+
if (keys.length !== 1) return INVALID_OPERAND;
|
|
4316
|
+
if (keys[0] === "lit") {
|
|
4317
|
+
const value = asConditionValue(operand.lit);
|
|
4318
|
+
return value === void 0 ? INVALID_OPERAND : value;
|
|
4319
|
+
}
|
|
4320
|
+
if (keys[0] !== "attr") return INVALID_OPERAND;
|
|
4321
|
+
const attr = operand.attr;
|
|
4322
|
+
if (typeof attr !== "string" || attr.length === 0) return INVALID_OPERAND;
|
|
4323
|
+
const path = attr.split(".");
|
|
4324
|
+
let value;
|
|
4325
|
+
let rest;
|
|
4326
|
+
if (path[0] === "subject") {
|
|
4327
|
+
value = subject;
|
|
4328
|
+
rest = path.slice(1);
|
|
4329
|
+
} else if (path[0] === "doc") {
|
|
4330
|
+
if (path[1] !== request.scope) return;
|
|
4331
|
+
value = conditions.scopeState;
|
|
4332
|
+
rest = path.slice(2);
|
|
4333
|
+
} else if (path[0] === "action" && path[1] === "input") {
|
|
4334
|
+
value = conditions.actionInput;
|
|
4335
|
+
rest = path.slice(2);
|
|
4336
|
+
} else return;
|
|
4337
|
+
for (const segment of rest) {
|
|
4338
|
+
if (!isPlainValue(value) || !Object.hasOwn(value, segment)) return;
|
|
4339
|
+
value = value[segment];
|
|
4340
|
+
}
|
|
4341
|
+
return asConditionValue(value);
|
|
4342
|
+
}
|
|
4343
|
+
/**
|
|
4344
|
+
* Total order within one type: numbers numerically, strings by code point.
|
|
4345
|
+
* Everything else, including mixed types, does not order.
|
|
4346
|
+
*/
|
|
4347
|
+
function compareValues(left, right) {
|
|
4348
|
+
if (typeof left === "number" && typeof right === "number") return left < right ? -1 : left > right ? 1 : 0;
|
|
4349
|
+
if (typeof left === "string" && typeof right === "string") {
|
|
4350
|
+
const leftPoints = Array.from(left);
|
|
4351
|
+
const rightPoints = Array.from(right);
|
|
4352
|
+
const shared = Math.min(leftPoints.length, rightPoints.length);
|
|
4353
|
+
for (let i = 0; i < shared; i++) {
|
|
4354
|
+
const a = leftPoints[i].codePointAt(0) ?? 0;
|
|
4355
|
+
const b = rightPoints[i].codePointAt(0) ?? 0;
|
|
4356
|
+
if (a !== b) return a < b ? -1 : 1;
|
|
4357
|
+
}
|
|
4358
|
+
return leftPoints.length === rightPoints.length ? 0 : leftPoints.length < rightPoints.length ? -1 : 1;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
/**
|
|
4362
|
+
* Tri-state evaluation: undefined marks a structurally invalid node, which
|
|
4363
|
+
* poisons the whole tree to false at the top — and a structurally malformed
|
|
4364
|
+
* operand poisons its condition the same way. Both are distinct from an
|
|
4365
|
+
* operand whose path fails to resolve, which is a valid comparison that is
|
|
4366
|
+
* false. The distinction keeps `not` from widening over malformed input.
|
|
4367
|
+
*/
|
|
4368
|
+
function evaluateNode(node, subject, request, conditions) {
|
|
4369
|
+
if (!isPlainValue(node)) return;
|
|
4370
|
+
const keys = Object.keys(node);
|
|
4371
|
+
if (keys.length !== 1) return;
|
|
4372
|
+
const kind = keys[0];
|
|
4373
|
+
const body = node[kind];
|
|
4374
|
+
switch (kind) {
|
|
4375
|
+
case "eq":
|
|
4376
|
+
case "ne":
|
|
4377
|
+
case "lt":
|
|
4378
|
+
case "lte":
|
|
4379
|
+
case "gt":
|
|
4380
|
+
case "gte": {
|
|
4381
|
+
if (!Array.isArray(body) || body.length !== 2) return;
|
|
4382
|
+
const left = resolveOperand(body[0], subject, request, conditions);
|
|
4383
|
+
const right = resolveOperand(body[1], subject, request, conditions);
|
|
4384
|
+
if (left === INVALID_OPERAND || right === INVALID_OPERAND) return;
|
|
4385
|
+
if (left === void 0 || right === void 0) return false;
|
|
4386
|
+
if (kind === "eq") return left === right;
|
|
4387
|
+
if (kind === "ne") return left !== right;
|
|
4388
|
+
const order = compareValues(left, right);
|
|
4389
|
+
if (order === void 0) return false;
|
|
4390
|
+
switch (kind) {
|
|
4391
|
+
case "lt": return order < 0;
|
|
4392
|
+
case "lte": return order <= 0;
|
|
4393
|
+
case "gt": return order > 0;
|
|
4394
|
+
case "gte": return order >= 0;
|
|
4395
|
+
}
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
case "in":
|
|
4399
|
+
case "notIn": {
|
|
4400
|
+
if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return;
|
|
4401
|
+
const left = resolveOperand(body[0], subject, request, conditions);
|
|
4402
|
+
if (left === INVALID_OPERAND) return;
|
|
4403
|
+
const elements = body[1].map((element) => resolveOperand(element, subject, request, conditions));
|
|
4404
|
+
if (elements.some((value) => value === INVALID_OPERAND)) return;
|
|
4405
|
+
if (left === void 0) return false;
|
|
4406
|
+
const found = elements.some((value) => value !== void 0 && value === left);
|
|
4407
|
+
return kind === "in" ? found : !found;
|
|
4408
|
+
}
|
|
4409
|
+
case "exists": {
|
|
4410
|
+
const value = resolveOperand(body, subject, request, conditions);
|
|
4411
|
+
if (value === INVALID_OPERAND) return;
|
|
4412
|
+
return value !== void 0;
|
|
4413
|
+
}
|
|
4414
|
+
case "and":
|
|
4415
|
+
case "or": {
|
|
4416
|
+
if (!Array.isArray(body)) return;
|
|
4417
|
+
let result = kind === "and";
|
|
4418
|
+
for (const child of body) {
|
|
4419
|
+
const value = evaluateNode(child, subject, request, conditions);
|
|
4420
|
+
if (value === void 0) return;
|
|
4421
|
+
if (kind === "and") result = result && value;
|
|
4422
|
+
else result = result || value;
|
|
4423
|
+
}
|
|
4424
|
+
return result;
|
|
4425
|
+
}
|
|
4426
|
+
case "not": {
|
|
4427
|
+
const value = evaluateNode(body, subject, request, conditions);
|
|
4428
|
+
return value === void 0 ? void 0 : !value;
|
|
4429
|
+
}
|
|
4430
|
+
default: return;
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
/**
|
|
4434
|
+
* Evaluates a version-1 condition. Deterministic, total, and pure: any input
|
|
4435
|
+
* shape yields a boolean and never throws, and a malformed condition is
|
|
4436
|
+
* false. These are consensus semantics, versioned by `PHAuthState.version`;
|
|
4437
|
+
* changing them requires a new version.
|
|
4438
|
+
*/
|
|
4439
|
+
function evaluateCondition(condition, subject, request, conditions) {
|
|
4440
|
+
return evaluateNode(condition, subject, request, conditions) === true;
|
|
4441
|
+
}
|
|
4442
|
+
/** Whether a capability's scope reaches the requested one. */
|
|
4443
|
+
function scopeCovers(scope, requested) {
|
|
4444
|
+
return scope === void 0 || scope === "*" || scope === requested;
|
|
4445
|
+
}
|
|
4446
|
+
/**
|
|
4447
|
+
* Whether one grant answers this request.
|
|
4448
|
+
*
|
|
4449
|
+
* A grant allowing execute on a scope also allows reading it: executing an
|
|
4450
|
+
* operation means reading the state it applies to, so permitting the write while
|
|
4451
|
+
* withholding the read would describe an access nobody could use. The converse
|
|
4452
|
+
* does not hold -- a read grant confers no write.
|
|
4453
|
+
*
|
|
4454
|
+
* Only an allow carries across. A deny on execute withholds the write and says
|
|
4455
|
+
* nothing about the read, so a policy locking writes down does not silently
|
|
4456
|
+
* revoke a read grant sitting before it. The operation list is not consulted
|
|
4457
|
+
* either: it restricts which operations may be executed, not whether the scope
|
|
4458
|
+
* is visible, and a read carries no operation to match against.
|
|
4459
|
+
*
|
|
4460
|
+
* Reads are not consensus -- no replica records a read, and the two read call
|
|
4461
|
+
* sites are both inside the read gate -- so this rule is not part of what makes
|
|
4462
|
+
* an operation valid, and does not need a policy version of its own.
|
|
4463
|
+
*/
|
|
4464
|
+
function grantAnswers(grant, request) {
|
|
4465
|
+
if (capabilityCovers(grant.capability, request)) return true;
|
|
4466
|
+
return request.verb === "read" && grant.effect === "allow" && grant.capability.can === "execute" && scopeCovers(grant.capability.scope, request.scope);
|
|
4467
|
+
}
|
|
4468
|
+
function capabilityCovers(capability, request) {
|
|
4469
|
+
if (capability.can !== request.verb) return false;
|
|
4470
|
+
if (!scopeCovers(capability.scope, request.scope)) return false;
|
|
4471
|
+
if (capability.can === "execute") {
|
|
4472
|
+
if (capability.operation === void 0) return true;
|
|
4473
|
+
return request.operation !== void 0 && capability.operation.includes(request.operation);
|
|
4474
|
+
}
|
|
4475
|
+
return true;
|
|
4476
|
+
}
|
|
4477
|
+
function principalMatches(principal, subject, request, groups, conditions) {
|
|
4478
|
+
if ("anyone" in principal) return true;
|
|
4479
|
+
if ("address" in principal) return subject.address !== void 0 && subject.address.toLowerCase() === principal.address.toLowerCase();
|
|
4480
|
+
if ("group" in principal) {
|
|
4481
|
+
if (groups === void 0 || subject.address === void 0) return false;
|
|
4482
|
+
const group = groups[principal.group];
|
|
4483
|
+
if (group === void 0 || !Array.isArray(group.members)) return false;
|
|
4484
|
+
const address = subject.address.toLowerCase();
|
|
4485
|
+
return group.members.some((member) => member.toLowerCase() === address);
|
|
4486
|
+
}
|
|
4487
|
+
if ("match" in principal) {
|
|
4488
|
+
if (conditions === void 0) return false;
|
|
4489
|
+
return evaluateCondition(principal.match, subject, request, conditions);
|
|
4490
|
+
}
|
|
4491
|
+
return false;
|
|
4492
|
+
}
|
|
4493
|
+
/**
|
|
4494
|
+
* Evaluates a v1 grant stack: default deny, last applicable grant wins, and
|
|
4495
|
+
* reports which grant decided it. Group principals match only against a
|
|
4496
|
+
* supplied groups map, and `where` clauses and { match } principals evaluate
|
|
4497
|
+
* only against a supplied condition context; a grant that uses an unsupplied
|
|
4498
|
+
* feature never applies.
|
|
4499
|
+
*/
|
|
4500
|
+
function evaluateGrantStack(grants, subject, request, groups, conditions) {
|
|
4501
|
+
let applicable;
|
|
4502
|
+
for (const grant of grants) {
|
|
4503
|
+
if (grant.where !== void 0) {
|
|
4504
|
+
if (conditions === void 0) continue;
|
|
4505
|
+
if (!evaluateCondition(grant.where, subject, request, conditions)) continue;
|
|
4506
|
+
}
|
|
4507
|
+
if (grantAnswers(grant, request) && principalMatches(grant.principal, subject, request, groups, conditions)) applicable = grant;
|
|
4508
|
+
}
|
|
4509
|
+
if (applicable === void 0) return {
|
|
4510
|
+
decision: "deny",
|
|
4511
|
+
refusal: "no-applicable-grant"
|
|
4512
|
+
};
|
|
4513
|
+
if (applicable.effect === "deny") return {
|
|
4514
|
+
decision: "deny",
|
|
4515
|
+
refusal: "denied-by-grant",
|
|
4516
|
+
grantId: applicable.id
|
|
4517
|
+
};
|
|
4518
|
+
return { decision: "allow" };
|
|
4519
|
+
}
|
|
3746
4520
|
const documentModelFileExtension = "phdm";
|
|
3747
4521
|
const documentModelInitialLocalState = {};
|
|
3748
4522
|
const documentModelInitialGlobalState = {
|
|
@@ -4324,7 +5098,306 @@ const documentModelGlobalState = {
|
|
|
4324
5098
|
};
|
|
4325
5099
|
const HASH_ALGORITHM_SHA1 = "sha1";
|
|
4326
5100
|
const HASH_ENCODING_BASE64 = "base64";
|
|
4327
|
-
|
|
5101
|
+
/**
|
|
5102
|
+
* Creates a default PHAuthState
|
|
5103
|
+
*/
|
|
5104
|
+
function defaultAuthState() {
|
|
5105
|
+
return {
|
|
5106
|
+
version: 0,
|
|
5107
|
+
grants: []
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Creates a default PHDocumentState
|
|
5112
|
+
*/
|
|
5113
|
+
function defaultDocumentState() {
|
|
5114
|
+
return {
|
|
5115
|
+
version: 0,
|
|
5116
|
+
hash: {
|
|
5117
|
+
algorithm: HASH_ALGORITHM_SHA1,
|
|
5118
|
+
encoding: HASH_ENCODING_BASE64
|
|
5119
|
+
}
|
|
5120
|
+
};
|
|
5121
|
+
}
|
|
5122
|
+
/**
|
|
5123
|
+
* Creates a default PHBaseState with auth and document properties
|
|
5124
|
+
*/
|
|
5125
|
+
function defaultBaseState() {
|
|
5126
|
+
return {
|
|
5127
|
+
auth: defaultAuthState(),
|
|
5128
|
+
document: defaultDocumentState()
|
|
5129
|
+
};
|
|
5130
|
+
}
|
|
5131
|
+
/**
|
|
5132
|
+
* Creates a PHAuthState with the given properties
|
|
5133
|
+
*/
|
|
5134
|
+
function createAuthState(auth) {
|
|
5135
|
+
return {
|
|
5136
|
+
...defaultAuthState(),
|
|
5137
|
+
...auth
|
|
5138
|
+
};
|
|
5139
|
+
}
|
|
5140
|
+
/**
|
|
5141
|
+
* Creates a PHDocumentState with the given properties
|
|
5142
|
+
*/
|
|
5143
|
+
function createDocumentState(document) {
|
|
5144
|
+
return {
|
|
5145
|
+
...defaultDocumentState(),
|
|
5146
|
+
...document
|
|
5147
|
+
};
|
|
5148
|
+
}
|
|
5149
|
+
/**
|
|
5150
|
+
* Creates a PHBaseState with the given auth and document properties
|
|
5151
|
+
*/
|
|
5152
|
+
function createBaseState(auth, document) {
|
|
5153
|
+
return {
|
|
5154
|
+
auth: createAuthState(auth),
|
|
5155
|
+
document: createDocumentState(document)
|
|
5156
|
+
};
|
|
5157
|
+
}
|
|
5158
|
+
/**
|
|
5159
|
+
* Backfills the auth scope to the default for legacy documents serialized with
|
|
5160
|
+
* an empty `auth`. Replaces only `state.auth`. Idempotent.
|
|
5161
|
+
*/
|
|
5162
|
+
function backfillAuthState(state) {
|
|
5163
|
+
return {
|
|
5164
|
+
...state,
|
|
5165
|
+
auth: createAuthState(state.auth)
|
|
5166
|
+
};
|
|
5167
|
+
}
|
|
5168
|
+
function defaultGlobalState() {
|
|
5169
|
+
return {
|
|
5170
|
+
...defaultBaseState(),
|
|
5171
|
+
author: {
|
|
5172
|
+
name: "",
|
|
5173
|
+
website: ""
|
|
5174
|
+
},
|
|
5175
|
+
description: "",
|
|
5176
|
+
extension: "",
|
|
5177
|
+
id: "",
|
|
5178
|
+
name: "",
|
|
5179
|
+
specifications: []
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
function defaultLocalState() {
|
|
5183
|
+
return {};
|
|
5184
|
+
}
|
|
5185
|
+
function createGlobalState(state) {
|
|
5186
|
+
return {
|
|
5187
|
+
...defaultGlobalState(),
|
|
5188
|
+
...state || {}
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
function createLocalState(state) {
|
|
5192
|
+
return {
|
|
5193
|
+
...defaultLocalState(),
|
|
5194
|
+
...state || {}
|
|
5195
|
+
};
|
|
5196
|
+
}
|
|
5197
|
+
function createState(baseState, globalState, localState) {
|
|
5198
|
+
return {
|
|
5199
|
+
...createBaseState(baseState?.auth, baseState?.document),
|
|
5200
|
+
global: createGlobalState(globalState),
|
|
5201
|
+
local: createLocalState(localState)
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
5204
|
+
/**
|
|
5205
|
+
* Destructuring a null input (reachable via raw synced operations) would
|
|
5206
|
+
* store an engine-specific TypeError message on the error operation.
|
|
5207
|
+
*/
|
|
5208
|
+
function assertActionInputShape(input) {
|
|
5209
|
+
if (!isPlainValue(input)) throw new InvalidActionInputError({ input: "must be an object" });
|
|
5210
|
+
}
|
|
5211
|
+
function withGrants(document, grants) {
|
|
5212
|
+
return {
|
|
5213
|
+
...document,
|
|
5214
|
+
state: {
|
|
5215
|
+
...document.state,
|
|
5216
|
+
auth: {
|
|
5217
|
+
...document.state.auth,
|
|
5218
|
+
grants
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
};
|
|
5222
|
+
}
|
|
5223
|
+
const P256_PUBKEY_MULTICODEC = [128, 36];
|
|
5224
|
+
const DID_KEY_PREFIX = "did:key:z";
|
|
5225
|
+
function bytesEqual(a, b) {
|
|
5226
|
+
if (a.length !== b.length) return false;
|
|
5227
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
5228
|
+
return true;
|
|
5229
|
+
}
|
|
5230
|
+
/**
|
|
5231
|
+
* True when `signerKey` (an ActionSigner app key, a did:key) identifies the same
|
|
5232
|
+
* key recorded as the document creator. Returns false
|
|
5233
|
+
* when there is no creator (empty JWK) or no signer key.
|
|
5234
|
+
*/
|
|
5235
|
+
function isDocumentCreator(creatorKey, signerKey) {
|
|
5236
|
+
if (!creatorKey?.x || !creatorKey.y) return false;
|
|
5237
|
+
if (!signerKey || !signerKey.startsWith(DID_KEY_PREFIX)) return false;
|
|
5238
|
+
const decoded = base58Decode(signerKey.slice(9));
|
|
5239
|
+
if (!decoded || decoded.length !== 35) return false;
|
|
5240
|
+
if (decoded[0] !== P256_PUBKEY_MULTICODEC[0] || decoded[1] !== P256_PUBKEY_MULTICODEC[1]) return false;
|
|
5241
|
+
const parityPrefix = decoded[2];
|
|
5242
|
+
if (parityPrefix !== 2 && parityPrefix !== 3) return false;
|
|
5243
|
+
const didX = decoded.subarray(3, 35);
|
|
5244
|
+
const jwkX = base64UrlToBytes(creatorKey.x);
|
|
5245
|
+
const jwkY = base64UrlToBytes(creatorKey.y);
|
|
5246
|
+
if (jwkX.length !== 32 || jwkY.length !== 32) return false;
|
|
5247
|
+
if (!bytesEqual(didX, jwkX)) return false;
|
|
5248
|
+
return (jwkY[31] & 1) === 1 === (parityPrefix === 3);
|
|
5249
|
+
}
|
|
5250
|
+
/**
|
|
5251
|
+
* Sets the initial policy. Valid only while the auth scope is uninitialized
|
|
5252
|
+
* (version 0). The input version is the policy language version and must be an
|
|
5253
|
+
* integer >= 1; 0 is reserved for the uninitialized state. On a signed-header
|
|
5254
|
+
* document it must be signed by the document creator (`header.sig.publicKey`).
|
|
5255
|
+
* A creator-less policy must include a grant permitting execute on the auth
|
|
5256
|
+
* scope, or it would be born locked out.
|
|
5257
|
+
*/
|
|
5258
|
+
function applyInitializeAuthAction(document, action) {
|
|
5259
|
+
assertActionInputShape(action.input);
|
|
5260
|
+
const { version, grants } = action.input;
|
|
5261
|
+
if (!Number.isInteger(version) || version < 1) throw new InvalidAuthVersionError(document.header.id, version);
|
|
5262
|
+
if (document.state.auth.version !== 0) throw new AuthAlreadyInitializedError(document.header.id);
|
|
5263
|
+
if (!Array.isArray(grants)) throw new InvalidActionInputError({ grants: "must be an array" });
|
|
5264
|
+
const creatorKey = document.header.sig.publicKey;
|
|
5265
|
+
const signerKey = action.context?.signer?.app.key;
|
|
5266
|
+
const hasCreator = Boolean(creatorKey.kty || creatorKey.x || creatorKey.y);
|
|
5267
|
+
if (hasCreator && !isDocumentCreator(creatorKey, signerKey)) throw new AuthInitializerNotCreatorError(document.header.id);
|
|
5268
|
+
const creator = hasCreator ? signerKey : void 0;
|
|
5269
|
+
assertValidInitialGrants(grants, document.header.documentType, creator);
|
|
5270
|
+
return {
|
|
5271
|
+
...document,
|
|
5272
|
+
state: {
|
|
5273
|
+
...document.state,
|
|
5274
|
+
auth: createAuthState(creator ? {
|
|
5275
|
+
version,
|
|
5276
|
+
grants,
|
|
5277
|
+
creator
|
|
5278
|
+
} : {
|
|
5279
|
+
version,
|
|
5280
|
+
grants
|
|
5281
|
+
})
|
|
5282
|
+
}
|
|
5283
|
+
};
|
|
5284
|
+
}
|
|
5285
|
+
/** Upserts a grant by id: replaces in place if present, otherwise appends. */
|
|
5286
|
+
function applySetGrantAction(document, action) {
|
|
5287
|
+
assertActionInputShape(action.input);
|
|
5288
|
+
const { grant } = action.input;
|
|
5289
|
+
const grants = document.state.auth.grants;
|
|
5290
|
+
assertValidGrantUpsert(grant, grants, document.header.documentType, document.state.auth.creator);
|
|
5291
|
+
return withGrants(document, grants.some((g) => g.id === grant.id) ? grants.map((g) => g.id === grant.id ? grant : g) : [...grants, grant]);
|
|
5292
|
+
}
|
|
5293
|
+
/**
|
|
5294
|
+
* Removes a grant by id; throws if the id is not present or if the removal
|
|
5295
|
+
* would leave a creator-less policy with no auth-administration grant.
|
|
5296
|
+
*/
|
|
5297
|
+
function applyRemoveGrantAction(document, action) {
|
|
5298
|
+
assertActionInputShape(action.input);
|
|
5299
|
+
const { id } = action.input;
|
|
5300
|
+
const { grants, creator } = document.state.auth;
|
|
5301
|
+
if (!grants.some((g) => g.id === id)) throw new GrantNotFoundError(id);
|
|
5302
|
+
const next = grants.filter((g) => g.id !== id);
|
|
5303
|
+
assertAuthAdministrationRetained(creator, grants, next, id);
|
|
5304
|
+
return withGrants(document, next);
|
|
5305
|
+
}
|
|
5306
|
+
/**
|
|
5307
|
+
* Moves a grant by id to a new index. Order is load-bearing (the last
|
|
5308
|
+
* applicable grant wins), so the relative order of the other grants is kept.
|
|
5309
|
+
* The target index is clamped to the valid range; an unknown id throws.
|
|
5310
|
+
*
|
|
5311
|
+
* Order alone decides which grant wins, so a move can take administration away
|
|
5312
|
+
* without changing the list's contents. It carries the same retention rule as
|
|
5313
|
+
* the two mutation paths.
|
|
5314
|
+
*/
|
|
5315
|
+
function applyMoveGrantAction(document, action) {
|
|
5316
|
+
assertActionInputShape(action.input);
|
|
5317
|
+
const { id, index } = action.input;
|
|
5318
|
+
const { grants, creator } = document.state.auth;
|
|
5319
|
+
const from = grants.findIndex((g) => g.id === id);
|
|
5320
|
+
if (from === -1) throw new GrantNotFoundError(id);
|
|
5321
|
+
const next = [...grants];
|
|
5322
|
+
const [moved] = next.splice(from, 1);
|
|
5323
|
+
const to = Math.max(0, Math.min(index, next.length));
|
|
5324
|
+
next.splice(to, 0, moved);
|
|
5325
|
+
assertAuthAdministrationRetained(creator, grants, next, id);
|
|
5326
|
+
return withGrants(document, next);
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Dispatches an auth-scope action to its handler. This is the auth scope's
|
|
5330
|
+
* dedicated reducer: it is applied by the base reducer instead of the model
|
|
5331
|
+
* reducer, mirroring the document-scope platform handlers. Unknown types are a
|
|
5332
|
+
* no-op, matching the model-reducer default.
|
|
5333
|
+
*/
|
|
5334
|
+
function applyAuthAction(document, action) {
|
|
5335
|
+
switch (action.type) {
|
|
5336
|
+
case "INITIALIZE_AUTH": return applyInitializeAuthAction(document, action);
|
|
5337
|
+
case "SET_GRANT": return applySetGrantAction(document, action);
|
|
5338
|
+
case "REMOVE_GRANT": return applyRemoveGrantAction(document, action);
|
|
5339
|
+
case "MOVE_GRANT": return applyMoveGrantAction(document, action);
|
|
5340
|
+
default: return document;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
/**
|
|
5344
|
+
* Because only creators can initialize auth scopes, we must verify that either
|
|
5345
|
+
* the document has no auth or the version and creator match.
|
|
5346
|
+
*/
|
|
5347
|
+
function assertAuthPreservedOnDuplicate(documentId, source, duplicated) {
|
|
5348
|
+
if (!source || source.version === 0) return;
|
|
5349
|
+
if (duplicated === void 0 || duplicated.version !== source.version || duplicated.creator !== source.creator) throw new AuthPolicyNotPreservedError(documentId);
|
|
5350
|
+
}
|
|
5351
|
+
/**
|
|
5352
|
+
* The auth scope a state snapshot may install, given the policy already there.
|
|
5353
|
+
*
|
|
5354
|
+
* `applyAuthAction` is the validated door onto `state.auth`, but a whole-state
|
|
5355
|
+
* snapshot (UPGRADE_DOCUMENT's `initialState`, LOAD_STATE's `data`) replaces the
|
|
5356
|
+
* scope wholesale and is authorized as a `document`-scope write. Without this,
|
|
5357
|
+
* a subject holding `execute` on `document` and no auth grant at all can install
|
|
5358
|
+
* a policy of its choosing, name itself `creator` (which exempts the policy from
|
|
5359
|
+
* the retention rule for good), or wipe an existing policy by carrying the
|
|
5360
|
+
* default uninitialized one.
|
|
5361
|
+
*
|
|
5362
|
+
* Three cases:
|
|
5363
|
+
*
|
|
5364
|
+
* - the snapshot carries no policy, or an uninitialized one: the document
|
|
5365
|
+
* keeps the policy it has, so a default-state upgrade cannot reset it;
|
|
5366
|
+
* - the document is uninitialized and the snapshot carries a policy: the
|
|
5367
|
+
* policy is installed after the same validation genesis applies, so it
|
|
5368
|
+
* cannot be born locked out;
|
|
5369
|
+
* - both carry a policy: they must agree. A duplicate preserves its source's
|
|
5370
|
+
* policy (see {@link assertAuthPreservedOnDuplicate}), and anything else is
|
|
5371
|
+
* an attempt to replace one policy with another.
|
|
5372
|
+
*/
|
|
5373
|
+
function resolveSnapshotAuth(documentId, documentType, current, incoming) {
|
|
5374
|
+
const currentAuth = current ?? createAuthState({
|
|
5375
|
+
version: 0,
|
|
5376
|
+
grants: []
|
|
5377
|
+
});
|
|
5378
|
+
if (!incoming || !incoming.version) return currentAuth;
|
|
5379
|
+
if (currentAuth.version !== 0) {
|
|
5380
|
+
if (stringify$1(incoming) !== stringify$1(currentAuth)) throw new AuthPolicyNotPreservedError(documentId);
|
|
5381
|
+
return incoming;
|
|
5382
|
+
}
|
|
5383
|
+
if (!Array.isArray(incoming.grants)) throw new InvalidActionInputError({ grants: "must be an array" });
|
|
5384
|
+
assertValidInitialGrants(incoming.grants, documentType, incoming.creator);
|
|
5385
|
+
return incoming;
|
|
5386
|
+
}
|
|
5387
|
+
/** UNDO, REDO and PRUNE are rejected on the auth scope. */
|
|
5388
|
+
function assertAuthScopeActionAllowed(action) {
|
|
5389
|
+
if (action.scope === "auth" && [
|
|
5390
|
+
"UNDO",
|
|
5391
|
+
"REDO",
|
|
5392
|
+
"PRUNE"
|
|
5393
|
+
].includes(action.type)) throw new AuthActionNotAllowedError(action.type);
|
|
5394
|
+
}
|
|
5395
|
+
/**
|
|
5396
|
+
* True iff authorization rejected the action.
|
|
5397
|
+
*/
|
|
5398
|
+
function isDenied(operation) {
|
|
5399
|
+
return operation.deniedReason !== void 0;
|
|
5400
|
+
}
|
|
4328
5401
|
const BaseDocumentHeaderSchema = object({
|
|
4329
5402
|
id: string(),
|
|
4330
5403
|
name: string(),
|
|
@@ -4455,6 +5528,7 @@ function createDocumentScopeOperations(header, state) {
|
|
|
4455
5528
|
function baseCreateDocument(createState, initialState, documentType = "") {
|
|
4456
5529
|
const state = createState(initialState);
|
|
4457
5530
|
const header = createPresignedHeader(generateId(), documentType);
|
|
5531
|
+
header.protocolVersions = { "base-reducer": 2 };
|
|
4458
5532
|
return {
|
|
4459
5533
|
header,
|
|
4460
5534
|
state,
|
|
@@ -4478,6 +5552,18 @@ function hashDocumentStateForScope(document, scope = "global") {
|
|
|
4478
5552
|
* Uses the V2 model where consecutive NOOPs form chains.
|
|
4479
5553
|
* Unlike V1 garbageCollect, this preserves ALL operations but marks which to apply.
|
|
4480
5554
|
*/
|
|
5555
|
+
/**
|
|
5556
|
+
* The base-reducer protocol version a document is written against. Every
|
|
5557
|
+
* document carries one, set from the CREATE_DOCUMENT input. A header without
|
|
5558
|
+
* one was built outside that path, and choosing a version on its behalf would
|
|
5559
|
+
* replay the document through a reducer it was not written for, so it is an
|
|
5560
|
+
* error rather than a default.
|
|
5561
|
+
*/
|
|
5562
|
+
function baseReducerVersion(header) {
|
|
5563
|
+
const version = header.protocolVersions?.["base-reducer"];
|
|
5564
|
+
if (typeof version !== "number") throw new Error(`Document ${header.id} carries no base-reducer protocol version`);
|
|
5565
|
+
return version;
|
|
5566
|
+
}
|
|
4481
5567
|
function garbageCollectV2(sortedOperations) {
|
|
4482
5568
|
const result = [];
|
|
4483
5569
|
let noopChainLength = 0;
|
|
@@ -4494,9 +5580,26 @@ function garbageCollectV2(sortedOperations) {
|
|
|
4494
5580
|
const defaultCreateState = (state) => {
|
|
4495
5581
|
return state;
|
|
4496
5582
|
};
|
|
5583
|
+
/**
|
|
5584
|
+
* Records an operation in the history without applying it, which is what a
|
|
5585
|
+
* denied operation needs: it occupies its index and contributes no state.
|
|
5586
|
+
*
|
|
5587
|
+
* The scope defaults to the action's own, and is passed explicitly by a rebuild
|
|
5588
|
+
* that is walking one stream and does not want to trust the action's copy.
|
|
5589
|
+
*/
|
|
5590
|
+
function appendWithoutApplying(document, operation, scope = operation.action.scope) {
|
|
5591
|
+
return {
|
|
5592
|
+
...document,
|
|
5593
|
+
operations: {
|
|
5594
|
+
...document.operations,
|
|
5595
|
+
[scope]: [...document.operations[scope] ?? [], operation]
|
|
5596
|
+
}
|
|
5597
|
+
};
|
|
5598
|
+
}
|
|
4497
5599
|
function replayDocument(initialState, operations, reducer, header, dispatch, skipHeaderOperations = {}, options) {
|
|
4498
5600
|
const { checkHashes = true, reuseOperationResultingState, operationResultingStateParser = parseResultingState, skipIndexValidation } = options || {};
|
|
4499
|
-
|
|
5601
|
+
const backfilledInitialState = backfillAuthState(initialState);
|
|
5602
|
+
let documentState = backfilledInitialState;
|
|
4500
5603
|
const operationsToReplay = [];
|
|
4501
5604
|
const allScopes = new Set([
|
|
4502
5605
|
...Object.keys(operations),
|
|
@@ -4531,12 +5634,13 @@ function replayDocument(initialState, operations, reducer, header, dispatch, ski
|
|
|
4531
5634
|
const document = {
|
|
4532
5635
|
header,
|
|
4533
5636
|
state: defaultCreateState(documentState),
|
|
4534
|
-
initialState,
|
|
5637
|
+
initialState: backfilledInitialState,
|
|
4535
5638
|
operations: initialOperations,
|
|
4536
5639
|
clipboard: []
|
|
4537
5640
|
};
|
|
4538
5641
|
let result = document;
|
|
4539
5642
|
if (operationsToReplay.length) result = operationsToReplay.reduce((document, operation) => {
|
|
5643
|
+
if (isDenied(operation)) return updateHeaderRevision(appendWithoutApplying(document, operation), operation.action.scope, operation.timestampUtcMs);
|
|
4540
5644
|
return reducer(document, operation.action, dispatch, {
|
|
4541
5645
|
ignoreSkipOperations: true,
|
|
4542
5646
|
checkHashes,
|
|
@@ -4818,13 +5922,15 @@ function redoOperation(document, action, skip) {
|
|
|
4818
5922
|
});
|
|
4819
5923
|
}
|
|
4820
5924
|
function loadStateOperation(document, action) {
|
|
5925
|
+
const loaded = backfillAuthState(action.state.data);
|
|
5926
|
+
loaded.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, loaded.auth);
|
|
4821
5927
|
return {
|
|
4822
5928
|
...document,
|
|
4823
5929
|
header: {
|
|
4824
5930
|
...document.header,
|
|
4825
5931
|
name: action.state.name
|
|
4826
5932
|
},
|
|
4827
|
-
state:
|
|
5933
|
+
state: loaded
|
|
4828
5934
|
};
|
|
4829
5935
|
}
|
|
4830
5936
|
function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
|
|
@@ -4998,12 +6104,13 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
|
|
|
4998
6104
|
function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
4999
6105
|
const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main" } = options;
|
|
5000
6106
|
let _action = actionFromAction(action);
|
|
6107
|
+
assertAuthScopeActionAllowed(_action);
|
|
5001
6108
|
let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
|
|
5002
6109
|
let newDocument = { ...document };
|
|
5003
6110
|
let reuseLastOperationIndex = false;
|
|
5004
6111
|
const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
|
|
5005
6112
|
if (isUndoRedo(_action)) {
|
|
5006
|
-
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ??
|
|
6113
|
+
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? baseReducerVersion(document.header));
|
|
5007
6114
|
_action = transformedAction;
|
|
5008
6115
|
skipValue = calculatedSkip;
|
|
5009
6116
|
newDocument = processedDocument;
|
|
@@ -5019,7 +6126,7 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
|
5019
6126
|
branch
|
|
5020
6127
|
};
|
|
5021
6128
|
newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
|
|
5022
|
-
const protocolVersion = options.protocolVersion ??
|
|
6129
|
+
const protocolVersion = options.protocolVersion ?? baseReducerVersion(document.header);
|
|
5023
6130
|
if (isUndo(action) && protocolVersion < 2) return processUndoOperation(newDocument, action.scope, customReducer);
|
|
5024
6131
|
const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
|
|
5025
6132
|
if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
|
|
@@ -5045,6 +6152,13 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
|
5045
6152
|
}
|
|
5046
6153
|
newDocument = create(newDocument, (draft) => {
|
|
5047
6154
|
try {
|
|
6155
|
+
if (_action.scope === "auth") {
|
|
6156
|
+
const authState = applyAuthAction(newDocument, _action).state;
|
|
6157
|
+
unsafe(() => {
|
|
6158
|
+
draft.state = castDraft(authState);
|
|
6159
|
+
});
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
5048
6162
|
const newState = customReducer(draft.state, _action, dispatch);
|
|
5049
6163
|
if (newState) unsafe(() => {
|
|
5050
6164
|
draft.state = castDraft(newState);
|
|
@@ -5834,14 +6948,27 @@ const documentModelStateReducer = (state, action) => {
|
|
|
5834
6948
|
}
|
|
5835
6949
|
};
|
|
5836
6950
|
const documentModelReducer = createReducer(documentModelStateReducer);
|
|
6951
|
+
/**
|
|
6952
|
+
* Canonical document-model version normalization: documents stamped with 0
|
|
6953
|
+
* or nothing at all predate versioning and are treated as version 1, the
|
|
6954
|
+
* same version the registry assigns unversioned modules. Every consumer
|
|
6955
|
+
* that resolves a module or compares versions must use this rule; resolving
|
|
6956
|
+
* 0 to "latest" instead re-pins a legacy document's history to whichever
|
|
6957
|
+
* module happens to be newest.
|
|
6958
|
+
*/
|
|
6959
|
+
function normalizeDocumentModelVersion(version) {
|
|
6960
|
+
return version && version > 0 ? version : 1;
|
|
6961
|
+
}
|
|
5837
6962
|
function applyInitialState(document, action) {
|
|
5838
6963
|
const input = action.input;
|
|
5839
6964
|
const newState = input.initialState || input.state;
|
|
5840
6965
|
if (newState) {
|
|
5841
|
-
|
|
6966
|
+
const merged = backfillAuthState({
|
|
5842
6967
|
...document.state,
|
|
5843
6968
|
...newState
|
|
5844
|
-
};
|
|
6969
|
+
});
|
|
6970
|
+
merged.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, merged.auth);
|
|
6971
|
+
document.state = merged;
|
|
5845
6972
|
document.initialState = document.state;
|
|
5846
6973
|
}
|
|
5847
6974
|
}
|
|
@@ -5901,7 +7028,6 @@ function computeUpgradeTransitions(manifest, fromVersion, toVersion) {
|
|
|
5901
7028
|
}
|
|
5902
7029
|
return transitions;
|
|
5903
7030
|
}
|
|
5904
|
-
const NON_DOMAIN_SCOPES$1 = new Set(["auth", "document"]);
|
|
5905
7031
|
function highestReducerVersion(reducers) {
|
|
5906
7032
|
const keys = Object.keys(reducers).map(Number);
|
|
5907
7033
|
if (keys.length === 0) throw new Error("VersionedReplayConfig.reducers must not be empty");
|
|
@@ -5929,15 +7055,15 @@ function highestReducerVersion(reducers) {
|
|
|
5929
7055
|
*/
|
|
5930
7056
|
function replayDocumentVersioned(initialState, operations, config, header, dispatch, options) {
|
|
5931
7057
|
const { checkHashes = true, skipIndexValidation } = options || {};
|
|
5932
|
-
const protocolVersion = header
|
|
7058
|
+
const protocolVersion = baseReducerVersion(header);
|
|
5933
7059
|
const spine = (operations["document"] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
5934
7060
|
const upgrades = spine.filter((op) => op.action.type === "UPGRADE_DOCUMENT");
|
|
5935
7061
|
const legacyFallback = () => {
|
|
5936
|
-
const
|
|
7062
|
+
const replayOps = Object.fromEntries(Object.entries(operations).filter(([s]) => s !== "document"));
|
|
5937
7063
|
const latestVersion = highestReducerVersion(config.reducers);
|
|
5938
7064
|
const reducer = config.reducers[latestVersion];
|
|
5939
7065
|
return {
|
|
5940
|
-
...replayDocument(initialState,
|
|
7066
|
+
...replayDocument(initialState, replayOps, reducer, header, dispatch, {}, options),
|
|
5941
7067
|
operations
|
|
5942
7068
|
};
|
|
5943
7069
|
};
|
|
@@ -5961,14 +7087,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5961
7087
|
const a = op.action;
|
|
5962
7088
|
return a.input.fromVersion > 0 && a.input.fromVersion < a.input.toVersion;
|
|
5963
7089
|
});
|
|
5964
|
-
const
|
|
7090
|
+
const replayScopes = Object.keys(operations).filter((s) => s !== "document");
|
|
5965
7091
|
const scopeOps = {};
|
|
5966
|
-
for (const s of
|
|
7092
|
+
for (const s of replayScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
5967
7093
|
const boundaries = validatedUpgrades.map((upgradeOp) => {
|
|
5968
7094
|
const revisionSnapshot = upgradeOp.action.input.revision;
|
|
5969
7095
|
const upgradeTimestamp = upgradeOp.timestampUtcMs;
|
|
5970
7096
|
const boundary = {};
|
|
5971
|
-
for (const s of
|
|
7097
|
+
for (const s of replayScopes) {
|
|
5972
7098
|
const ops = scopeOps[s] ?? [];
|
|
5973
7099
|
if (revisionSnapshot !== void 0) {
|
|
5974
7100
|
const rev = revisionSnapshot[s] ?? 0;
|
|
@@ -5986,7 +7112,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5986
7112
|
}
|
|
5987
7113
|
return boundary;
|
|
5988
7114
|
});
|
|
5989
|
-
for (let i = 1; i < boundaries.length; i++) for (const s of
|
|
7115
|
+
for (let i = 1; i < boundaries.length; i++) for (const s of replayScopes) {
|
|
5990
7116
|
const prev = boundaries[i - 1]?.[s] ?? 0;
|
|
5991
7117
|
const curr = boundaries[i]?.[s] ?? 0;
|
|
5992
7118
|
if (boundaries[i]) boundaries[i][s] = Math.max(prev, curr);
|
|
@@ -5998,10 +7124,11 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5998
7124
|
]);
|
|
5999
7125
|
const initialOperations = {};
|
|
6000
7126
|
for (const s of allScopes) initialOperations[s] = [];
|
|
7127
|
+
const backfilledSeed = backfillAuthState(seedState);
|
|
6001
7128
|
let document = {
|
|
6002
7129
|
header,
|
|
6003
|
-
state:
|
|
6004
|
-
initialState:
|
|
7130
|
+
state: backfilledSeed,
|
|
7131
|
+
initialState: backfilledSeed,
|
|
6005
7132
|
operations: initialOperations,
|
|
6006
7133
|
clipboard: []
|
|
6007
7134
|
};
|
|
@@ -6009,17 +7136,15 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6009
7136
|
const segmentEndHashPerScope = /* @__PURE__ */ new Map();
|
|
6010
7137
|
for (let k = 0; k <= validatedUpgrades.length; k++) {
|
|
6011
7138
|
const reducer = config.reducers[currentVersion];
|
|
6012
|
-
if (!reducer)
|
|
6013
|
-
|
|
6014
|
-
throw new Error(`No reducer registered for document version ${currentVersion}. Available versions: ${available}`);
|
|
6015
|
-
}
|
|
6016
|
-
for (const s of domainScopes) {
|
|
7139
|
+
if (!reducer) throw new UnsupportedDocumentModelVersionError(header.documentType, currentVersion, Object.keys(config.reducers).map(Number).sort((a, b) => a - b));
|
|
7140
|
+
for (const s of replayScopes) {
|
|
6017
7141
|
const ops = scopeOps[s] ?? [];
|
|
6018
7142
|
const segStart = k === 0 ? 0 : boundaries[k - 1]?.[s] ?? 0;
|
|
6019
7143
|
const segEnd = k < validatedUpgrades.length ? boundaries[k]?.[s] ?? ops.length : ops.length;
|
|
6020
7144
|
const segOps = ops.slice(segStart, segEnd);
|
|
6021
7145
|
for (const op of segOps) {
|
|
6022
|
-
document =
|
|
7146
|
+
if (isDenied(op)) document = updateHeaderRevision(appendWithoutApplying(document, op, s), s, op.timestampUtcMs);
|
|
7147
|
+
else document = reducer(document, op.action, dispatch, {
|
|
6023
7148
|
ignoreSkipOperations: true,
|
|
6024
7149
|
checkHashes,
|
|
6025
7150
|
skipIndexValidation,
|
|
@@ -6036,6 +7161,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6036
7161
|
if (!spineOp) continue;
|
|
6037
7162
|
const spineActionType = spineOp.action.type;
|
|
6038
7163
|
if (spineActionType === "CREATE_DOCUMENT" || spineActionType === "UPGRADE_DOCUMENT") continue;
|
|
7164
|
+
if (isDenied(spineOp)) continue;
|
|
6039
7165
|
if (spineActionType === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, spineOp.action);
|
|
6040
7166
|
else document = reducer(document, spineOp.action, dispatch, {
|
|
6041
7167
|
ignoreSkipOperations: true,
|
|
@@ -6066,7 +7192,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6066
7192
|
}
|
|
6067
7193
|
};
|
|
6068
7194
|
if (!checkHashes) {
|
|
6069
|
-
const allReplayedOps =
|
|
7195
|
+
const allReplayedOps = replayScopes.flatMap((s) => scopeOps[s] ?? []);
|
|
6070
7196
|
for (const scope of Object.keys(document.state)) {
|
|
6071
7197
|
const capturedHash = segmentEndHashPerScope.get(scope);
|
|
6072
7198
|
const scopeHash = capturedHash !== void 0 ? capturedHash : hashDocumentStateForScope(document, scope);
|
|
@@ -6108,7 +7234,6 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6108
7234
|
}
|
|
6109
7235
|
};
|
|
6110
7236
|
}
|
|
6111
|
-
const NON_DOMAIN_SCOPES = new Set(["auth", "document"]);
|
|
6112
7237
|
function zipAsync(data) {
|
|
6113
7238
|
return new Promise((resolve, reject) => {
|
|
6114
7239
|
zip(data, (err, out) => err ? reject(err) : resolve(out));
|
|
@@ -6187,7 +7312,7 @@ async function parseZipData(data) {
|
|
|
6187
7312
|
async function loadFromZipData(data, reducer, options) {
|
|
6188
7313
|
const { initialState, header, clearedOperations } = await parseZipData(data);
|
|
6189
7314
|
return {
|
|
6190
|
-
...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) =>
|
|
7315
|
+
...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => scope !== "document")), reducer, header, void 0, {}, options),
|
|
6191
7316
|
operations: clearedOperations
|
|
6192
7317
|
};
|
|
6193
7318
|
}
|
|
@@ -6210,96 +7335,6 @@ const documentModelLoadFromInput = (input) => {
|
|
|
6210
7335
|
const documentModelSaveToFileHandle = (document, input) => {
|
|
6211
7336
|
return baseSaveToFileHandle(document, input);
|
|
6212
7337
|
};
|
|
6213
|
-
/**
|
|
6214
|
-
* Creates a default PHAuthState
|
|
6215
|
-
*/
|
|
6216
|
-
function defaultAuthState() {
|
|
6217
|
-
return {};
|
|
6218
|
-
}
|
|
6219
|
-
/**
|
|
6220
|
-
* Creates a default PHDocumentState
|
|
6221
|
-
*/
|
|
6222
|
-
function defaultDocumentState() {
|
|
6223
|
-
return {
|
|
6224
|
-
version: 0,
|
|
6225
|
-
hash: {
|
|
6226
|
-
algorithm: HASH_ALGORITHM_SHA1,
|
|
6227
|
-
encoding: HASH_ENCODING_BASE64
|
|
6228
|
-
}
|
|
6229
|
-
};
|
|
6230
|
-
}
|
|
6231
|
-
/**
|
|
6232
|
-
* Creates a default PHBaseState with auth and document properties
|
|
6233
|
-
*/
|
|
6234
|
-
function defaultBaseState() {
|
|
6235
|
-
return {
|
|
6236
|
-
auth: defaultAuthState(),
|
|
6237
|
-
document: defaultDocumentState()
|
|
6238
|
-
};
|
|
6239
|
-
}
|
|
6240
|
-
/**
|
|
6241
|
-
* Creates a PHAuthState with the given properties
|
|
6242
|
-
*/
|
|
6243
|
-
function createAuthState(auth) {
|
|
6244
|
-
return {
|
|
6245
|
-
...defaultAuthState(),
|
|
6246
|
-
...auth
|
|
6247
|
-
};
|
|
6248
|
-
}
|
|
6249
|
-
/**
|
|
6250
|
-
* Creates a PHDocumentState with the given properties
|
|
6251
|
-
*/
|
|
6252
|
-
function createDocumentState(document) {
|
|
6253
|
-
return {
|
|
6254
|
-
...defaultDocumentState(),
|
|
6255
|
-
...document
|
|
6256
|
-
};
|
|
6257
|
-
}
|
|
6258
|
-
/**
|
|
6259
|
-
* Creates a PHBaseState with the given auth and document properties
|
|
6260
|
-
*/
|
|
6261
|
-
function createBaseState(auth, document) {
|
|
6262
|
-
return {
|
|
6263
|
-
auth: createAuthState(auth),
|
|
6264
|
-
document: createDocumentState(document)
|
|
6265
|
-
};
|
|
6266
|
-
}
|
|
6267
|
-
function defaultGlobalState() {
|
|
6268
|
-
return {
|
|
6269
|
-
...defaultBaseState(),
|
|
6270
|
-
author: {
|
|
6271
|
-
name: "",
|
|
6272
|
-
website: ""
|
|
6273
|
-
},
|
|
6274
|
-
description: "",
|
|
6275
|
-
extension: "",
|
|
6276
|
-
id: "",
|
|
6277
|
-
name: "",
|
|
6278
|
-
specifications: []
|
|
6279
|
-
};
|
|
6280
|
-
}
|
|
6281
|
-
function defaultLocalState() {
|
|
6282
|
-
return {};
|
|
6283
|
-
}
|
|
6284
|
-
function createGlobalState(state) {
|
|
6285
|
-
return {
|
|
6286
|
-
...defaultGlobalState(),
|
|
6287
|
-
...state || {}
|
|
6288
|
-
};
|
|
6289
|
-
}
|
|
6290
|
-
function createLocalState(state) {
|
|
6291
|
-
return {
|
|
6292
|
-
...defaultLocalState(),
|
|
6293
|
-
...state || {}
|
|
6294
|
-
};
|
|
6295
|
-
}
|
|
6296
|
-
function createState(baseState, globalState, localState) {
|
|
6297
|
-
return {
|
|
6298
|
-
...createBaseState(baseState?.auth, baseState?.document),
|
|
6299
|
-
global: createGlobalState(globalState),
|
|
6300
|
-
local: createLocalState(localState)
|
|
6301
|
-
};
|
|
6302
|
-
}
|
|
6303
7338
|
//#endregion
|
|
6304
7339
|
//#region ../document-model/dist/index.js
|
|
6305
7340
|
const isStateOfType$1 = (state) => {
|
|
@@ -7378,6 +8413,6 @@ const driveDocumentModelModule = {
|
|
|
7378
8413
|
}
|
|
7379
8414
|
};
|
|
7380
8415
|
//#endregion
|
|
7381
|
-
export {
|
|
8416
|
+
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 };
|
|
7382
8417
|
|
|
7383
|
-
//# sourceMappingURL=document-drive-
|
|
8418
|
+
//# sourceMappingURL=document-drive-ChLoGFvU.mjs.map
|