@powerhousedao/powerhouse-vetra-packages 6.2.2-dev.8 → 6.2.2-dev.81
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-5_Eh4a2Z.js} +3 -2
- package/dist/browser/assets/{projection-entry-DVec8YRh.js → projection-entry-DgFDnTMt.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-Bpm1qc2o.js} +916 -200
- package/dist/browser/connect-Bpm1qc2o.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-DzuQgnp9.js} +2388 -1848
- package/dist/browser/dist-DzuQgnp9.js.map +1 -0
- package/dist/browser/{document-drive-IdFV1QXL.js → document-drive-BVYCdu_5.js} +988 -124
- package/dist/browser/document-drive-BVYCdu_5.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-BhVC9JGB.js +25 -0
- package/dist/browser/editor-BhVC9JGB.js.map +1 -0
- package/dist/browser/{editor-DiZF3ao3.js → editor-Cr2SUFnl.js} +671 -236
- package/dist/browser/editor-Cr2SUFnl.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-EDdZl1fJ.js} +73 -46
- package/dist/browser/folder-view-EDdZl1fJ.js.map +1 -0
- package/dist/browser/{graphql-editor-ZLRO_8Ce.js → graphql-editor-BjkthIT9.js} +8 -7
- package/dist/browser/{graphql-editor-ZLRO_8Ce.js.map → graphql-editor-BjkthIT9.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-nLgiw_aY.js} +4 -4
- package/dist/browser/{json-editor-DBuZ6gN7.js.map → json-editor-nLgiw_aY.js.map} +1 -1
- package/dist/browser/{lib-C-f3izXz.js → lib-ewnRj2dJ.js} +13 -4
- package/dist/browser/lib-ewnRj2dJ.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-IiwC01DT.js} +8 -18
- package/dist/browser/state-schemas-IiwC01DT.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-DzMvrvtH.mjs} +916 -200
- package/dist/node/connect-DzMvrvtH.mjs.map +1 -0
- package/dist/node/{dist-DJEK2WVi.mjs → dist-CWqCyPep.mjs} +2391 -1846
- package/dist/node/dist-CWqCyPep.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-lf_0SP-H.mjs} +988 -124
- package/dist/node/document-drive-lf_0SP-H.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-B1KhoF1f.mjs → editor-BHFgv5Sn.mjs} +672 -237
- package/dist/node/editor-BHFgv5Sn.mjs.map +1 -0
- package/dist/node/editor-DSlBxXEG.mjs +25 -0
- package/dist/node/editor-DSlBxXEG.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-BU4jkZT4.mjs} +73 -46
- package/dist/node/folder-view-BU4jkZT4.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-CScEOmFM.mjs} +7 -7
- package/dist/node/{graphql-editor-C5VLRqEf.mjs.map → graphql-editor-CScEOmFM.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-BkdBLGDx.mjs} +4 -4
- package/dist/node/{json-editor-iI2s9a_4.mjs.map → json-editor-BkdBLGDx.mjs.map} +1 -1
- package/dist/node/{main-D82dkD7g.mjs → main-B1tRsYOp.mjs} +15 -6
- package/dist/node/main-B1tRsYOp.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-BCTGWiNB.mjs} +10 -20
- package/dist/{browser/state-schemas-BjQKM498.js.map → node/state-schemas-BCTGWiNB.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 { 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-
|
|
592
|
+
//#region ../shared/dist/utils-aaTmfxFW.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,103 @@ 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: toTransportSignerUser(signer.user) } : {},
|
|
3048
|
+
...signer.app ? { app: toTransportSignerApp(signer.app) } : {},
|
|
3049
|
+
signatures: (signer.signatures ?? []).map(serializeSignature)
|
|
3050
|
+
};
|
|
3051
|
+
return Object.keys(projected).length > 0 ? projected : void 0;
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* The identity, projected for the same reason the context around it is.
|
|
3055
|
+
*
|
|
3056
|
+
* A signer is handed in by the app, so what reaches here is only as narrow as
|
|
3057
|
+
* whoever built it: an identity carrying a session's DID, credential or profile
|
|
3058
|
+
* alongside the address is refused by `ReactorSignerUserInput`, and that
|
|
3059
|
+
* refusal takes the whole submission with it.
|
|
3060
|
+
*
|
|
3061
|
+
* The compiler cannot stand in for this. `UserActionSigner` already declares
|
|
3062
|
+
* exactly these three fields, but excess-property checks apply to fresh object
|
|
3063
|
+
* literals and never to a variable of a wider type, so a wide identity assigned
|
|
3064
|
+
* to the narrow type passes untouched.
|
|
3065
|
+
*/
|
|
3066
|
+
function toTransportSignerUser(user) {
|
|
3067
|
+
return {
|
|
3068
|
+
address: user.address,
|
|
3069
|
+
networkId: user.networkId,
|
|
3070
|
+
chainId: user.chainId
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
/** The signing app, projected on the same rule as the identity above. */
|
|
3074
|
+
function toTransportSignerApp(app) {
|
|
3075
|
+
return {
|
|
3076
|
+
name: app.name,
|
|
3077
|
+
key: app.key
|
|
3078
|
+
};
|
|
3079
|
+
}
|
|
2949
3080
|
var InvalidActionInputError = class extends Error {
|
|
2950
3081
|
data;
|
|
2951
3082
|
constructor(data) {
|
|
@@ -2978,6 +3109,75 @@ var DowngradeNotSupportedError = class extends Error {
|
|
|
2978
3109
|
this.toVersion = toVersion;
|
|
2979
3110
|
}
|
|
2980
3111
|
};
|
|
3112
|
+
/**
|
|
3113
|
+
* Thrown when INITIALIZE_AUTH is applied to an already-initialized auth scope.
|
|
3114
|
+
* The genesis action is valid only at auth revision zero.
|
|
3115
|
+
*/
|
|
3116
|
+
var AuthAlreadyInitializedError = class extends Error {
|
|
3117
|
+
documentId;
|
|
3118
|
+
constructor(documentId) {
|
|
3119
|
+
super(`Auth scope already initialized for document ${documentId}: INITIALIZE_AUTH is valid only at auth revision zero`);
|
|
3120
|
+
this.name = "AuthAlreadyInitializedError";
|
|
3121
|
+
this.documentId = documentId;
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
/**
|
|
3125
|
+
* Thrown when INITIALIZE_AUTH is not signed by the document creator (its signer
|
|
3126
|
+
* does not match `header.sig.publicKey`), so it cannot set the auth policy.
|
|
3127
|
+
*/
|
|
3128
|
+
var AuthInitializerNotCreatorError = class extends Error {
|
|
3129
|
+
documentId;
|
|
3130
|
+
constructor(documentId) {
|
|
3131
|
+
super(`INITIALIZE_AUTH for document ${documentId} must be signed by the document creator`);
|
|
3132
|
+
this.name = "AuthInitializerNotCreatorError";
|
|
3133
|
+
this.documentId = documentId;
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
/**
|
|
3137
|
+
* Thrown when INITIALIZE_AUTH carries a version below 1. Version 0 is reserved
|
|
3138
|
+
* for the uninitialized auth scope.
|
|
3139
|
+
*/
|
|
3140
|
+
var InvalidAuthVersionError = class extends Error {
|
|
3141
|
+
documentId;
|
|
3142
|
+
version;
|
|
3143
|
+
constructor(documentId, version) {
|
|
3144
|
+
super(`Invalid auth policy version ${version} for document ${documentId}: INITIALIZE_AUTH requires an integer version >= 1`);
|
|
3145
|
+
this.name = "InvalidAuthVersionError";
|
|
3146
|
+
this.documentId = documentId;
|
|
3147
|
+
this.version = version;
|
|
3148
|
+
}
|
|
3149
|
+
};
|
|
3150
|
+
/** Thrown when a duplicate would lose the source policy's version or creator binding. */
|
|
3151
|
+
var AuthPolicyNotPreservedError = class extends Error {
|
|
3152
|
+
documentId;
|
|
3153
|
+
constructor(documentId) {
|
|
3154
|
+
super(`Duplicating document ${documentId} would not preserve its auth policy: the copy loses the policy version or its creator binding`);
|
|
3155
|
+
this.name = "AuthPolicyNotPreservedError";
|
|
3156
|
+
this.documentId = documentId;
|
|
3157
|
+
}
|
|
3158
|
+
};
|
|
3159
|
+
/**
|
|
3160
|
+
* Thrown when a grant referenced by REMOVE_GRANT or MOVE_GRANT does not exist.
|
|
3161
|
+
*/
|
|
3162
|
+
var GrantNotFoundError = class extends Error {
|
|
3163
|
+
grantId;
|
|
3164
|
+
constructor(grantId) {
|
|
3165
|
+
super(`Grant not found in auth scope: ${grantId}`);
|
|
3166
|
+
this.name = "GrantNotFoundError";
|
|
3167
|
+
this.grantId = grantId;
|
|
3168
|
+
}
|
|
3169
|
+
};
|
|
3170
|
+
/**
|
|
3171
|
+
* Thrown when a disallowed action (UNDO, REDO, PRUNE) targets the auth scope.
|
|
3172
|
+
*/
|
|
3173
|
+
var AuthActionNotAllowedError = class extends Error {
|
|
3174
|
+
actionType;
|
|
3175
|
+
constructor(actionType) {
|
|
3176
|
+
super(`${actionType} is not permitted on the auth scope`);
|
|
3177
|
+
this.name = "AuthActionNotAllowedError";
|
|
3178
|
+
this.actionType = actionType;
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
2981
3181
|
var HashMismatchError = class extends Error {
|
|
2982
3182
|
_scope;
|
|
2983
3183
|
_document;
|
|
@@ -3004,6 +3204,25 @@ var HashMismatchError = class extends Error {
|
|
|
3004
3204
|
return this._operation;
|
|
3005
3205
|
}
|
|
3006
3206
|
};
|
|
3207
|
+
/**
|
|
3208
|
+
* Thrown when replay or import requires a document model version that is not
|
|
3209
|
+
* registered. Carries the data the UI needs to explain the mismatch.
|
|
3210
|
+
*/
|
|
3211
|
+
var UnsupportedDocumentModelVersionError = class extends Error {
|
|
3212
|
+
documentType;
|
|
3213
|
+
requiredVersion;
|
|
3214
|
+
availableVersions;
|
|
3215
|
+
constructor(documentType, requiredVersion, availableVersions) {
|
|
3216
|
+
super(`No reducer registered for document version ${requiredVersion}. Available versions: ${availableVersions.join(", ")}`);
|
|
3217
|
+
this.name = "UnsupportedDocumentModelVersionError";
|
|
3218
|
+
this.documentType = documentType;
|
|
3219
|
+
this.requiredVersion = requiredVersion;
|
|
3220
|
+
this.availableVersions = availableVersions;
|
|
3221
|
+
}
|
|
3222
|
+
static isError(error) {
|
|
3223
|
+
return Error.isError(error) && error.name === "UnsupportedDocumentModelVersionError";
|
|
3224
|
+
}
|
|
3225
|
+
};
|
|
3007
3226
|
const isDefinedNonNullAny$1 = (v) => v !== void 0 && v !== null;
|
|
3008
3227
|
any().refine((v) => isDefinedNonNullAny$1(v));
|
|
3009
3228
|
const Load_StateSchema = _enum(["LOAD_STATE"]);
|
|
@@ -3444,6 +3663,88 @@ const ConfigEntrySchema = object({
|
|
|
3444
3663
|
required: boolean().optional(),
|
|
3445
3664
|
default: boolean().optional()
|
|
3446
3665
|
});
|
|
3666
|
+
const PwaUrlPatternSchema = union([string(), strictObject({
|
|
3667
|
+
source: string(),
|
|
3668
|
+
flags: string().optional()
|
|
3669
|
+
}).refine((p) => {
|
|
3670
|
+
try {
|
|
3671
|
+
new RegExp(p.source, p.flags);
|
|
3672
|
+
return true;
|
|
3673
|
+
} catch {
|
|
3674
|
+
return false;
|
|
3675
|
+
}
|
|
3676
|
+
}, { message: "source/flags do not compile to a valid RegExp" })]);
|
|
3677
|
+
const PwaRuntimeCachingSchema = strictObject({
|
|
3678
|
+
urlPattern: PwaUrlPatternSchema,
|
|
3679
|
+
handler: _enum([
|
|
3680
|
+
"CacheFirst",
|
|
3681
|
+
"CacheOnly",
|
|
3682
|
+
"NetworkFirst",
|
|
3683
|
+
"NetworkOnly",
|
|
3684
|
+
"StaleWhileRevalidate"
|
|
3685
|
+
]),
|
|
3686
|
+
method: _enum([
|
|
3687
|
+
"GET",
|
|
3688
|
+
"POST",
|
|
3689
|
+
"PUT",
|
|
3690
|
+
"DELETE",
|
|
3691
|
+
"HEAD",
|
|
3692
|
+
"PATCH"
|
|
3693
|
+
]).optional(),
|
|
3694
|
+
options: strictObject({
|
|
3695
|
+
cacheName: string().optional(),
|
|
3696
|
+
networkTimeoutSeconds: number().optional(),
|
|
3697
|
+
expiration: strictObject({
|
|
3698
|
+
maxEntries: number().optional(),
|
|
3699
|
+
maxAgeSeconds: number().optional()
|
|
3700
|
+
}).optional(),
|
|
3701
|
+
cacheableResponse: strictObject({
|
|
3702
|
+
statuses: array(number()).optional(),
|
|
3703
|
+
headers: record(string(), string()).optional()
|
|
3704
|
+
}).optional()
|
|
3705
|
+
}).optional()
|
|
3706
|
+
});
|
|
3707
|
+
const PwaIconSchema = strictObject({
|
|
3708
|
+
src: string(),
|
|
3709
|
+
sizes: string().optional(),
|
|
3710
|
+
type: string().optional(),
|
|
3711
|
+
purpose: string().optional()
|
|
3712
|
+
});
|
|
3713
|
+
const PwaFileHandlerSchema = strictObject({
|
|
3714
|
+
accept: record(string(), array(string().regex(/^\./, "file extensions must start with '.'"))),
|
|
3715
|
+
icons: array(PwaIconSchema).optional(),
|
|
3716
|
+
launch_type: _enum(["single-client", "multiple-clients"]).optional()
|
|
3717
|
+
});
|
|
3718
|
+
const PwaConfigSchema = strictObject({
|
|
3719
|
+
manifest: strictObject({
|
|
3720
|
+
name: string().optional(),
|
|
3721
|
+
short_name: string().optional(),
|
|
3722
|
+
description: string().optional(),
|
|
3723
|
+
theme_color: string().optional(),
|
|
3724
|
+
background_color: string().optional(),
|
|
3725
|
+
display: _enum([
|
|
3726
|
+
"fullscreen",
|
|
3727
|
+
"standalone",
|
|
3728
|
+
"minimal-ui",
|
|
3729
|
+
"browser"
|
|
3730
|
+
]).optional(),
|
|
3731
|
+
start_url: string().optional(),
|
|
3732
|
+
scope: string().optional(),
|
|
3733
|
+
icons: array(PwaIconSchema).optional(),
|
|
3734
|
+
file_handlers: array(PwaFileHandlerSchema).optional(),
|
|
3735
|
+
launch_handler: strictObject({ client_mode: _enum([
|
|
3736
|
+
"auto",
|
|
3737
|
+
"focus-existing",
|
|
3738
|
+
"navigate-existing",
|
|
3739
|
+
"navigate-new"
|
|
3740
|
+
]) }).optional()
|
|
3741
|
+
}).optional(),
|
|
3742
|
+
globPatterns: array(string()).optional(),
|
|
3743
|
+
globIgnores: array(string()).optional(),
|
|
3744
|
+
maximumFileSizeToCacheInBytes: number().optional(),
|
|
3745
|
+
runtimeCaching: array(PwaRuntimeCachingSchema).optional(),
|
|
3746
|
+
navigateFallbackDenylist: array(PwaUrlPatternSchema).optional()
|
|
3747
|
+
});
|
|
3447
3748
|
object({
|
|
3448
3749
|
name: string(),
|
|
3449
3750
|
description: string().optional(),
|
|
@@ -3455,7 +3756,8 @@ object({
|
|
|
3455
3756
|
editors: PowerhouseModulesSchema,
|
|
3456
3757
|
processors: PowerhouseModulesSchema,
|
|
3457
3758
|
subgraphs: PowerhouseModulesSchema,
|
|
3458
|
-
config: array(ConfigEntrySchema).optional()
|
|
3759
|
+
config: array(ConfigEntrySchema).optional(),
|
|
3760
|
+
pwa: PwaConfigSchema.optional()
|
|
3459
3761
|
});
|
|
3460
3762
|
/**
|
|
3461
3763
|
* Cancels the last `count` operations.
|
|
@@ -3726,6 +4028,307 @@ const actions = {
|
|
|
3726
4028
|
...baseActions,
|
|
3727
4029
|
...documentModelActions
|
|
3728
4030
|
};
|
|
4031
|
+
const documentModelDocumentType = "powerhouse/document-model";
|
|
4032
|
+
/**
|
|
4033
|
+
* Thrown when a grant violates the v1 validation rules. The message is stored
|
|
4034
|
+
* on error operations, so it must be a pure function of the input.
|
|
4035
|
+
*/
|
|
4036
|
+
var InvalidGrantError = class extends Error {
|
|
4037
|
+
grantId;
|
|
4038
|
+
constructor(grantId, problem) {
|
|
4039
|
+
super(`Invalid grant "${grantId}": ${problem}`);
|
|
4040
|
+
this.name = "InvalidGrantError";
|
|
4041
|
+
this.grantId = grantId;
|
|
4042
|
+
}
|
|
4043
|
+
};
|
|
4044
|
+
/** Thrown for a `{ group }` principal on a group document: references never chain. */
|
|
4045
|
+
var GroupPrincipalNotAllowedError = class extends Error {
|
|
4046
|
+
grantId;
|
|
4047
|
+
constructor(grantId) {
|
|
4048
|
+
super(`Grant "${grantId}" uses a group principal on a group document: a group's auth scope cannot reference other groups`);
|
|
4049
|
+
this.name = "GroupPrincipalNotAllowedError";
|
|
4050
|
+
this.grantId = grantId;
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
/**
|
|
4054
|
+
* Thrown when a change would leave a creator-less policy with no grant
|
|
4055
|
+
* permitting execute on the auth scope. Without the creator carve-out no
|
|
4056
|
+
* subject could ever administer such a policy again.
|
|
4057
|
+
*/
|
|
4058
|
+
var AuthAdministrationLockoutError = class extends Error {
|
|
4059
|
+
grantId;
|
|
4060
|
+
constructor(grantId) {
|
|
4061
|
+
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`);
|
|
4062
|
+
this.name = "AuthAdministrationLockoutError";
|
|
4063
|
+
this.grantId = grantId;
|
|
4064
|
+
}
|
|
4065
|
+
};
|
|
4066
|
+
/**
|
|
4067
|
+
* Thrown when INITIALIZE_AUTH would create a creator-less policy with no grant
|
|
4068
|
+
* permitting execute on the auth scope. Such a policy would be born with no
|
|
4069
|
+
* subject able to administer it.
|
|
4070
|
+
*/
|
|
4071
|
+
var AuthAdministrationMissingError = class extends Error {
|
|
4072
|
+
constructor() {
|
|
4073
|
+
super("Initial grants include no reachable grant permitting execute on the auth scope: a policy with no creator must always include one");
|
|
4074
|
+
this.name = "AuthAdministrationMissingError";
|
|
4075
|
+
}
|
|
4076
|
+
};
|
|
4077
|
+
const GRANT_KEYS = new Set([
|
|
4078
|
+
"id",
|
|
4079
|
+
"description",
|
|
4080
|
+
"effect",
|
|
4081
|
+
"principal",
|
|
4082
|
+
"capability",
|
|
4083
|
+
"where"
|
|
4084
|
+
]);
|
|
4085
|
+
const PRINCIPAL_KINDS = new Set([
|
|
4086
|
+
"anyone",
|
|
4087
|
+
"address",
|
|
4088
|
+
"group",
|
|
4089
|
+
"match"
|
|
4090
|
+
]);
|
|
4091
|
+
const COMPARISON_CONDITION_KINDS = new Set([
|
|
4092
|
+
"eq",
|
|
4093
|
+
"ne",
|
|
4094
|
+
"lt",
|
|
4095
|
+
"lte",
|
|
4096
|
+
"gt",
|
|
4097
|
+
"gte"
|
|
4098
|
+
]);
|
|
4099
|
+
function isPlainValue(value) {
|
|
4100
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4101
|
+
}
|
|
4102
|
+
function operandProblem(value, capabilityScope, budget) {
|
|
4103
|
+
budget.nodes -= 1;
|
|
4104
|
+
if (budget.nodes < 0) return `condition exceeds 100 nodes`;
|
|
4105
|
+
if (!isPlainValue(value)) return "operand must be an object";
|
|
4106
|
+
const keys = Object.keys(value);
|
|
4107
|
+
if (keys.length !== 1) return "operand must have exactly one of attr or lit";
|
|
4108
|
+
if (keys[0] === "attr") {
|
|
4109
|
+
const attr = value.attr;
|
|
4110
|
+
if (typeof attr !== "string" || attr.length === 0) return "attr must be a non-empty string";
|
|
4111
|
+
if (capabilityScope !== void 0 && capabilityScope !== "*" && attr.startsWith("doc.")) {
|
|
4112
|
+
const pathScope = attr.split(".")[1] ?? "";
|
|
4113
|
+
if (pathScope !== capabilityScope) return `condition path "${attr}" reads scope "${pathScope}" but the capability covers only scope "${capabilityScope}"`;
|
|
4114
|
+
}
|
|
4115
|
+
return null;
|
|
4116
|
+
}
|
|
4117
|
+
if (keys[0] === "lit") {
|
|
4118
|
+
const lit = value.lit;
|
|
4119
|
+
if (lit !== null && typeof lit !== "string" && typeof lit !== "number" && typeof lit !== "boolean") return "lit must be a string, number, boolean, or null";
|
|
4120
|
+
if (typeof lit === "number" && !Number.isFinite(lit)) return "lit must be a finite number";
|
|
4121
|
+
if (typeof lit === "number" && Object.is(lit, -0)) return "lit must not be negative zero";
|
|
4122
|
+
return null;
|
|
4123
|
+
}
|
|
4124
|
+
return `unknown operand kind "${keys[0]}"`;
|
|
4125
|
+
}
|
|
4126
|
+
function conditionProblem(value, capabilityScope, depth, budget) {
|
|
4127
|
+
if (depth > 10) return `condition exceeds depth 10`;
|
|
4128
|
+
budget.nodes -= 1;
|
|
4129
|
+
if (budget.nodes < 0) return `condition exceeds 100 nodes`;
|
|
4130
|
+
if (!isPlainValue(value)) return "condition must be an object";
|
|
4131
|
+
const keys = Object.keys(value);
|
|
4132
|
+
if (keys.length !== 1) return "condition must have exactly one operator";
|
|
4133
|
+
const kind = keys[0];
|
|
4134
|
+
const body = value[kind];
|
|
4135
|
+
if (COMPARISON_CONDITION_KINDS.has(kind)) {
|
|
4136
|
+
if (!Array.isArray(body) || body.length !== 2) return `${kind} requires a pair of operands`;
|
|
4137
|
+
for (const operand of body) {
|
|
4138
|
+
const problem = operandProblem(operand, capabilityScope, budget);
|
|
4139
|
+
if (problem !== null) return problem;
|
|
4140
|
+
}
|
|
4141
|
+
return null;
|
|
4142
|
+
}
|
|
4143
|
+
if (kind === "in" || kind === "notIn") {
|
|
4144
|
+
if (!Array.isArray(body) || body.length !== 2 || !Array.isArray(body[1])) return `${kind} requires an operand and an operand list`;
|
|
4145
|
+
const first = operandProblem(body[0], capabilityScope, budget);
|
|
4146
|
+
if (first !== null) return first;
|
|
4147
|
+
for (const operand of body[1]) {
|
|
4148
|
+
const problem = operandProblem(operand, capabilityScope, budget);
|
|
4149
|
+
if (problem !== null) return problem;
|
|
4150
|
+
}
|
|
4151
|
+
return null;
|
|
4152
|
+
}
|
|
4153
|
+
if (kind === "exists") return operandProblem(body, capabilityScope, budget);
|
|
4154
|
+
if (kind === "and" || kind === "or") {
|
|
4155
|
+
if (!Array.isArray(body)) return `${kind} requires a condition list`;
|
|
4156
|
+
for (const child of body) {
|
|
4157
|
+
const problem = conditionProblem(child, capabilityScope, depth + 1, budget);
|
|
4158
|
+
if (problem !== null) return problem;
|
|
4159
|
+
}
|
|
4160
|
+
return null;
|
|
4161
|
+
}
|
|
4162
|
+
if (kind === "not") return conditionProblem(body, capabilityScope, depth + 1, budget);
|
|
4163
|
+
return `unknown condition operator "${kind}"`;
|
|
4164
|
+
}
|
|
4165
|
+
function principalProblem(value, capabilityScope) {
|
|
4166
|
+
if (!isPlainValue(value)) return "principal must be an object";
|
|
4167
|
+
const keys = Object.keys(value);
|
|
4168
|
+
if (keys.length !== 1 || !PRINCIPAL_KINDS.has(keys[0])) return "principal must have exactly one of anyone, address, group, or match";
|
|
4169
|
+
const kind = keys[0];
|
|
4170
|
+
if (kind === "anyone" && value.anyone !== true) return "anyone must be true";
|
|
4171
|
+
if (kind === "address") {
|
|
4172
|
+
const address = value.address;
|
|
4173
|
+
if (typeof address !== "string" || address.length === 0) return "address must be a non-empty string";
|
|
4174
|
+
}
|
|
4175
|
+
if (kind === "group") {
|
|
4176
|
+
const group = value.group;
|
|
4177
|
+
if (typeof group !== "string" || group.length === 0) return "group must be a non-empty string";
|
|
4178
|
+
}
|
|
4179
|
+
if (kind === "match") return conditionProblem(value.match, capabilityScope, 1, { nodes: 100 });
|
|
4180
|
+
return null;
|
|
4181
|
+
}
|
|
4182
|
+
function capabilityProblem(value) {
|
|
4183
|
+
if (!isPlainValue(value)) return "capability must be an object";
|
|
4184
|
+
const can = value.can;
|
|
4185
|
+
if (can !== "read" && can !== "execute") return "capability.can must be read or execute";
|
|
4186
|
+
const allowedKeys = can === "execute" ? [
|
|
4187
|
+
"can",
|
|
4188
|
+
"scope",
|
|
4189
|
+
"operation"
|
|
4190
|
+
] : ["can", "scope"];
|
|
4191
|
+
const unknownKeys = Object.keys(value).filter((key) => !allowedKeys.includes(key)).sort();
|
|
4192
|
+
if (unknownKeys.length > 0) return `unknown capability key "${unknownKeys[0]}"`;
|
|
4193
|
+
if (value.scope !== void 0) {
|
|
4194
|
+
if (typeof value.scope !== "string" || value.scope.length === 0) return "capability.scope must be a non-empty string";
|
|
4195
|
+
}
|
|
4196
|
+
if (can === "execute" && value.operation !== void 0) {
|
|
4197
|
+
const operation = value.operation;
|
|
4198
|
+
if (!Array.isArray(operation)) return "capability.operation must be an array";
|
|
4199
|
+
if (operation.length > 100) return `capability.operation exceeds 100 entries`;
|
|
4200
|
+
for (const entry of operation) if (typeof entry !== "string" || entry.length === 0) return "capability.operation entries must be non-empty strings";
|
|
4201
|
+
}
|
|
4202
|
+
return null;
|
|
4203
|
+
}
|
|
4204
|
+
/** Returns the first v1-rule violation, or null. Pure, total, deterministic. */
|
|
4205
|
+
function grantProblem(value) {
|
|
4206
|
+
if (!isPlainValue(value)) return "grant must be an object";
|
|
4207
|
+
const unknownKeys = Object.keys(value).filter((key) => !GRANT_KEYS.has(key)).sort();
|
|
4208
|
+
if (unknownKeys.length > 0) return `unknown grant key "${unknownKeys[0]}"`;
|
|
4209
|
+
if (typeof value.id !== "string" || value.id.length === 0) return "id must be a non-empty string";
|
|
4210
|
+
if (typeof value.description !== "string") return "description must be a string";
|
|
4211
|
+
if (value.effect !== "allow" && value.effect !== "deny") return "effect must be allow or deny";
|
|
4212
|
+
const capabilityValue = value.capability;
|
|
4213
|
+
const capability = capabilityProblem(capabilityValue);
|
|
4214
|
+
if (capability !== null) return capability;
|
|
4215
|
+
const capabilityScope = capabilityValue.scope;
|
|
4216
|
+
const principal = principalProblem(value.principal, capabilityScope);
|
|
4217
|
+
if (principal !== null) return principal;
|
|
4218
|
+
if (value.where !== void 0) return conditionProblem(value.where, capabilityScope, 1, { nodes: 100 });
|
|
4219
|
+
return null;
|
|
4220
|
+
}
|
|
4221
|
+
/** V1 shape rules plus the group-document group-principal ban. */
|
|
4222
|
+
function assertValidGrant(grant, documentType) {
|
|
4223
|
+
const grantId = isPlainValue(grant) && typeof grant.id === "string" ? grant.id : "";
|
|
4224
|
+
const problem = grantProblem(grant);
|
|
4225
|
+
if (problem !== null) throw new InvalidGrantError(grantId, problem);
|
|
4226
|
+
if (documentType === "powerhouse/reactor-group" && "group" in grant.principal) throw new GroupPrincipalNotAllowedError(grantId);
|
|
4227
|
+
}
|
|
4228
|
+
/**
|
|
4229
|
+
* Validates an initial grant list: the count cap, every grant, and — on a
|
|
4230
|
+
* creator-less policy — that some grant keeps the auth scope administrable.
|
|
4231
|
+
*/
|
|
4232
|
+
function assertValidInitialGrants(grants, documentType, creator) {
|
|
4233
|
+
if (grants.length > 100) throw new InvalidGrantError("", `policy exceeds 100 grants`);
|
|
4234
|
+
for (const grant of grants) assertValidGrant(grant, documentType);
|
|
4235
|
+
if (creator === void 0 && !administrationReachable(grants)) throw new AuthAdministrationMissingError();
|
|
4236
|
+
}
|
|
4237
|
+
/**
|
|
4238
|
+
* Validates a grant upsert: the grant itself, the count cap on append, and
|
|
4239
|
+
* administration retention. Retention is checked on an append as well as an
|
|
4240
|
+
* in-place replace, because a grant appended after the administration grant can
|
|
4241
|
+
* shadow it (evaluation is last-applicable-grant-wins) and so take
|
|
4242
|
+
* administration away without removing anything.
|
|
4243
|
+
*/
|
|
4244
|
+
function assertValidGrantUpsert(grant, existing, documentType, creator) {
|
|
4245
|
+
assertValidGrant(grant, documentType);
|
|
4246
|
+
const exists = existing.some((g) => g.id === grant.id);
|
|
4247
|
+
if (!exists && existing.length >= 100) throw new InvalidGrantError(grant.id, `policy exceeds 100 grants`);
|
|
4248
|
+
assertAuthAdministrationRetained(creator, existing, exists ? existing.map((g) => g.id === grant.id ? grant : g) : [...existing, grant], grant.id);
|
|
4249
|
+
}
|
|
4250
|
+
/**
|
|
4251
|
+
* The request whose coverage keeps a policy administrable: a subject who may
|
|
4252
|
+
* SET_GRANT can upsert any grant, so every other repair stays reachable.
|
|
4253
|
+
*/
|
|
4254
|
+
const AUTH_ADMINISTRATION_REQUEST = {
|
|
4255
|
+
verb: "execute",
|
|
4256
|
+
scope: "auth",
|
|
4257
|
+
operation: "SET_GRANT"
|
|
4258
|
+
};
|
|
4259
|
+
/**
|
|
4260
|
+
* Whether some subject can still administer the auth scope under this grant
|
|
4261
|
+
* list.
|
|
4262
|
+
*
|
|
4263
|
+
* Answers exactly what asking {@link evaluateGrantStack} per candidate grant
|
|
4264
|
+
* answered, in one reverse pass instead of one full stack scan per candidate.
|
|
4265
|
+
* Evaluation is last-applicable-grant-wins, so scanning from the end meets each
|
|
4266
|
+
* subject's deciding grant first: an anyone grant decides every subject not
|
|
4267
|
+
* already decided, and an allow reached that way is itself a grant that carries
|
|
4268
|
+
* administration. Only anyone and address principals are candidates, because
|
|
4269
|
+
* those are the ones v1 can match with no groups and no condition context; a
|
|
4270
|
+
* `where` condition or a group or match principal never applies.
|
|
4271
|
+
*/
|
|
4272
|
+
function administrationReachable(grants) {
|
|
4273
|
+
const shadowedAddresses = /* @__PURE__ */ new Set();
|
|
4274
|
+
for (let index = grants.length - 1; index >= 0; index -= 1) {
|
|
4275
|
+
const grant = grants[index];
|
|
4276
|
+
if (grant.where !== void 0 || !grantAnswers(grant, AUTH_ADMINISTRATION_REQUEST)) continue;
|
|
4277
|
+
const allows = grant.effect === "allow";
|
|
4278
|
+
if ("anyone" in grant.principal) return allows;
|
|
4279
|
+
if (!("address" in grant.principal)) continue;
|
|
4280
|
+
const address = grant.principal.address.toLowerCase();
|
|
4281
|
+
if (shadowedAddresses.has(address)) continue;
|
|
4282
|
+
if (allows) return true;
|
|
4283
|
+
shadowedAddresses.add(address);
|
|
4284
|
+
}
|
|
4285
|
+
return false;
|
|
4286
|
+
}
|
|
4287
|
+
/**
|
|
4288
|
+
* A creator-less policy must always retain a grant permitting execute on the
|
|
4289
|
+
* auth scope; on a signed document the creator carve-out keeps administration
|
|
4290
|
+
* reachable instead. Rejects a change that takes the last such grant away. A
|
|
4291
|
+
* policy already without one is left alone: the change is not what locks it.
|
|
4292
|
+
*/
|
|
4293
|
+
function assertAuthAdministrationRetained(creator, previous, next, grantId) {
|
|
4294
|
+
if (creator !== void 0) return;
|
|
4295
|
+
if (administrationReachable(previous) && !administrationReachable(next)) throw new AuthAdministrationLockoutError(grantId);
|
|
4296
|
+
}
|
|
4297
|
+
/** Whether a capability's scope reaches the requested one. */
|
|
4298
|
+
function scopeCovers(scope, requested) {
|
|
4299
|
+
return scope === void 0 || scope === "*" || scope === requested;
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* Whether one grant answers this request.
|
|
4303
|
+
*
|
|
4304
|
+
* A grant allowing execute on a scope also allows reading it: executing an
|
|
4305
|
+
* operation means reading the state it applies to, so permitting the write while
|
|
4306
|
+
* withholding the read would describe an access nobody could use. The converse
|
|
4307
|
+
* does not hold -- a read grant confers no write.
|
|
4308
|
+
*
|
|
4309
|
+
* Only an allow carries across. A deny on execute withholds the write and says
|
|
4310
|
+
* nothing about the read, so a policy locking writes down does not silently
|
|
4311
|
+
* revoke a read grant sitting before it. The operation list is not consulted
|
|
4312
|
+
* either: it restricts which operations may be executed, not whether the scope
|
|
4313
|
+
* is visible, and a read carries no operation to match against.
|
|
4314
|
+
*
|
|
4315
|
+
* Reads are not consensus -- no replica records a read, and the two read call
|
|
4316
|
+
* sites are both inside the read gate -- so this rule is not part of what makes
|
|
4317
|
+
* an operation valid, and does not need a policy version of its own.
|
|
4318
|
+
*/
|
|
4319
|
+
function grantAnswers(grant, request) {
|
|
4320
|
+
if (capabilityCovers(grant.capability, request)) return true;
|
|
4321
|
+
return request.verb === "read" && grant.effect === "allow" && grant.capability.can === "execute" && scopeCovers(grant.capability.scope, request.scope);
|
|
4322
|
+
}
|
|
4323
|
+
function capabilityCovers(capability, request) {
|
|
4324
|
+
if (capability.can !== request.verb) return false;
|
|
4325
|
+
if (!scopeCovers(capability.scope, request.scope)) return false;
|
|
4326
|
+
if (capability.can === "execute") {
|
|
4327
|
+
if (capability.operation === void 0) return true;
|
|
4328
|
+
return request.operation !== void 0 && capability.operation.includes(request.operation);
|
|
4329
|
+
}
|
|
4330
|
+
return true;
|
|
4331
|
+
}
|
|
3729
4332
|
const documentModelFileExtension = "phdm";
|
|
3730
4333
|
const documentModelInitialLocalState = {};
|
|
3731
4334
|
const documentModelInitialGlobalState = {
|
|
@@ -4307,7 +4910,306 @@ const documentModelGlobalState = {
|
|
|
4307
4910
|
};
|
|
4308
4911
|
const HASH_ALGORITHM_SHA1 = "sha1";
|
|
4309
4912
|
const HASH_ENCODING_BASE64 = "base64";
|
|
4310
|
-
|
|
4913
|
+
/**
|
|
4914
|
+
* Creates a default PHAuthState
|
|
4915
|
+
*/
|
|
4916
|
+
function defaultAuthState() {
|
|
4917
|
+
return {
|
|
4918
|
+
version: 0,
|
|
4919
|
+
grants: []
|
|
4920
|
+
};
|
|
4921
|
+
}
|
|
4922
|
+
/**
|
|
4923
|
+
* Creates a default PHDocumentState
|
|
4924
|
+
*/
|
|
4925
|
+
function defaultDocumentState() {
|
|
4926
|
+
return {
|
|
4927
|
+
version: 0,
|
|
4928
|
+
hash: {
|
|
4929
|
+
algorithm: HASH_ALGORITHM_SHA1,
|
|
4930
|
+
encoding: HASH_ENCODING_BASE64
|
|
4931
|
+
}
|
|
4932
|
+
};
|
|
4933
|
+
}
|
|
4934
|
+
/**
|
|
4935
|
+
* Creates a default PHBaseState with auth and document properties
|
|
4936
|
+
*/
|
|
4937
|
+
function defaultBaseState() {
|
|
4938
|
+
return {
|
|
4939
|
+
auth: defaultAuthState(),
|
|
4940
|
+
document: defaultDocumentState()
|
|
4941
|
+
};
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* Creates a PHAuthState with the given properties
|
|
4945
|
+
*/
|
|
4946
|
+
function createAuthState(auth) {
|
|
4947
|
+
return {
|
|
4948
|
+
...defaultAuthState(),
|
|
4949
|
+
...auth
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
/**
|
|
4953
|
+
* Creates a PHDocumentState with the given properties
|
|
4954
|
+
*/
|
|
4955
|
+
function createDocumentState(document) {
|
|
4956
|
+
return {
|
|
4957
|
+
...defaultDocumentState(),
|
|
4958
|
+
...document
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
/**
|
|
4962
|
+
* Creates a PHBaseState with the given auth and document properties
|
|
4963
|
+
*/
|
|
4964
|
+
function createBaseState(auth, document) {
|
|
4965
|
+
return {
|
|
4966
|
+
auth: createAuthState(auth),
|
|
4967
|
+
document: createDocumentState(document)
|
|
4968
|
+
};
|
|
4969
|
+
}
|
|
4970
|
+
/**
|
|
4971
|
+
* Backfills the auth scope to the default for legacy documents serialized with
|
|
4972
|
+
* an empty `auth`. Replaces only `state.auth`. Idempotent.
|
|
4973
|
+
*/
|
|
4974
|
+
function backfillAuthState(state) {
|
|
4975
|
+
return {
|
|
4976
|
+
...state,
|
|
4977
|
+
auth: createAuthState(state.auth)
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
function defaultGlobalState() {
|
|
4981
|
+
return {
|
|
4982
|
+
...defaultBaseState(),
|
|
4983
|
+
author: {
|
|
4984
|
+
name: "",
|
|
4985
|
+
website: ""
|
|
4986
|
+
},
|
|
4987
|
+
description: "",
|
|
4988
|
+
extension: "",
|
|
4989
|
+
id: "",
|
|
4990
|
+
name: "",
|
|
4991
|
+
specifications: []
|
|
4992
|
+
};
|
|
4993
|
+
}
|
|
4994
|
+
function defaultLocalState() {
|
|
4995
|
+
return {};
|
|
4996
|
+
}
|
|
4997
|
+
function createGlobalState(state) {
|
|
4998
|
+
return {
|
|
4999
|
+
...defaultGlobalState(),
|
|
5000
|
+
...state || {}
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
5003
|
+
function createLocalState(state) {
|
|
5004
|
+
return {
|
|
5005
|
+
...defaultLocalState(),
|
|
5006
|
+
...state || {}
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
function createState(baseState, globalState, localState) {
|
|
5010
|
+
return {
|
|
5011
|
+
...createBaseState(baseState?.auth, baseState?.document),
|
|
5012
|
+
global: createGlobalState(globalState),
|
|
5013
|
+
local: createLocalState(localState)
|
|
5014
|
+
};
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* Destructuring a null input (reachable via raw synced operations) would
|
|
5018
|
+
* store an engine-specific TypeError message on the error operation.
|
|
5019
|
+
*/
|
|
5020
|
+
function assertActionInputShape(input) {
|
|
5021
|
+
if (!isPlainValue(input)) throw new InvalidActionInputError({ input: "must be an object" });
|
|
5022
|
+
}
|
|
5023
|
+
function withGrants(document, grants) {
|
|
5024
|
+
return {
|
|
5025
|
+
...document,
|
|
5026
|
+
state: {
|
|
5027
|
+
...document.state,
|
|
5028
|
+
auth: {
|
|
5029
|
+
...document.state.auth,
|
|
5030
|
+
grants
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
};
|
|
5034
|
+
}
|
|
5035
|
+
const P256_PUBKEY_MULTICODEC = [128, 36];
|
|
5036
|
+
const DID_KEY_PREFIX = "did:key:z";
|
|
5037
|
+
function bytesEqual(a, b) {
|
|
5038
|
+
if (a.length !== b.length) return false;
|
|
5039
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
5040
|
+
return true;
|
|
5041
|
+
}
|
|
5042
|
+
/**
|
|
5043
|
+
* True when `signerKey` (an ActionSigner app key, a did:key) identifies the same
|
|
5044
|
+
* key recorded as the document creator. Returns false
|
|
5045
|
+
* when there is no creator (empty JWK) or no signer key.
|
|
5046
|
+
*/
|
|
5047
|
+
function isDocumentCreator(creatorKey, signerKey) {
|
|
5048
|
+
if (!creatorKey?.x || !creatorKey.y) return false;
|
|
5049
|
+
if (!signerKey || !signerKey.startsWith(DID_KEY_PREFIX)) return false;
|
|
5050
|
+
const decoded = base58Decode(signerKey.slice(9));
|
|
5051
|
+
if (!decoded || decoded.length !== 35) return false;
|
|
5052
|
+
if (decoded[0] !== P256_PUBKEY_MULTICODEC[0] || decoded[1] !== P256_PUBKEY_MULTICODEC[1]) return false;
|
|
5053
|
+
const parityPrefix = decoded[2];
|
|
5054
|
+
if (parityPrefix !== 2 && parityPrefix !== 3) return false;
|
|
5055
|
+
const didX = decoded.subarray(3, 35);
|
|
5056
|
+
const jwkX = base64UrlToBytes(creatorKey.x);
|
|
5057
|
+
const jwkY = base64UrlToBytes(creatorKey.y);
|
|
5058
|
+
if (jwkX.length !== 32 || jwkY.length !== 32) return false;
|
|
5059
|
+
if (!bytesEqual(didX, jwkX)) return false;
|
|
5060
|
+
return (jwkY[31] & 1) === 1 === (parityPrefix === 3);
|
|
5061
|
+
}
|
|
5062
|
+
/**
|
|
5063
|
+
* Sets the initial policy. Valid only while the auth scope is uninitialized
|
|
5064
|
+
* (version 0). The input version is the policy language version and must be an
|
|
5065
|
+
* integer >= 1; 0 is reserved for the uninitialized state. On a signed-header
|
|
5066
|
+
* document it must be signed by the document creator (`header.sig.publicKey`).
|
|
5067
|
+
* A creator-less policy must include a grant permitting execute on the auth
|
|
5068
|
+
* scope, or it would be born locked out.
|
|
5069
|
+
*/
|
|
5070
|
+
function applyInitializeAuthAction(document, action) {
|
|
5071
|
+
assertActionInputShape(action.input);
|
|
5072
|
+
const { version, grants } = action.input;
|
|
5073
|
+
if (!Number.isInteger(version) || version < 1) throw new InvalidAuthVersionError(document.header.id, version);
|
|
5074
|
+
if (document.state.auth.version !== 0) throw new AuthAlreadyInitializedError(document.header.id);
|
|
5075
|
+
if (!Array.isArray(grants)) throw new InvalidActionInputError({ grants: "must be an array" });
|
|
5076
|
+
const creatorKey = document.header.sig.publicKey;
|
|
5077
|
+
const signerKey = action.context?.signer?.app.key;
|
|
5078
|
+
const hasCreator = Boolean(creatorKey.kty || creatorKey.x || creatorKey.y);
|
|
5079
|
+
if (hasCreator && !isDocumentCreator(creatorKey, signerKey)) throw new AuthInitializerNotCreatorError(document.header.id);
|
|
5080
|
+
const creator = hasCreator ? signerKey : void 0;
|
|
5081
|
+
assertValidInitialGrants(grants, document.header.documentType, creator);
|
|
5082
|
+
return {
|
|
5083
|
+
...document,
|
|
5084
|
+
state: {
|
|
5085
|
+
...document.state,
|
|
5086
|
+
auth: createAuthState(creator ? {
|
|
5087
|
+
version,
|
|
5088
|
+
grants,
|
|
5089
|
+
creator
|
|
5090
|
+
} : {
|
|
5091
|
+
version,
|
|
5092
|
+
grants
|
|
5093
|
+
})
|
|
5094
|
+
}
|
|
5095
|
+
};
|
|
5096
|
+
}
|
|
5097
|
+
/** Upserts a grant by id: replaces in place if present, otherwise appends. */
|
|
5098
|
+
function applySetGrantAction(document, action) {
|
|
5099
|
+
assertActionInputShape(action.input);
|
|
5100
|
+
const { grant } = action.input;
|
|
5101
|
+
const grants = document.state.auth.grants;
|
|
5102
|
+
assertValidGrantUpsert(grant, grants, document.header.documentType, document.state.auth.creator);
|
|
5103
|
+
return withGrants(document, grants.some((g) => g.id === grant.id) ? grants.map((g) => g.id === grant.id ? grant : g) : [...grants, grant]);
|
|
5104
|
+
}
|
|
5105
|
+
/**
|
|
5106
|
+
* Removes a grant by id; throws if the id is not present or if the removal
|
|
5107
|
+
* would leave a creator-less policy with no auth-administration grant.
|
|
5108
|
+
*/
|
|
5109
|
+
function applyRemoveGrantAction(document, action) {
|
|
5110
|
+
assertActionInputShape(action.input);
|
|
5111
|
+
const { id } = action.input;
|
|
5112
|
+
const { grants, creator } = document.state.auth;
|
|
5113
|
+
if (!grants.some((g) => g.id === id)) throw new GrantNotFoundError(id);
|
|
5114
|
+
const next = grants.filter((g) => g.id !== id);
|
|
5115
|
+
assertAuthAdministrationRetained(creator, grants, next, id);
|
|
5116
|
+
return withGrants(document, next);
|
|
5117
|
+
}
|
|
5118
|
+
/**
|
|
5119
|
+
* Moves a grant by id to a new index. Order is load-bearing (the last
|
|
5120
|
+
* applicable grant wins), so the relative order of the other grants is kept.
|
|
5121
|
+
* The target index is clamped to the valid range; an unknown id throws.
|
|
5122
|
+
*
|
|
5123
|
+
* Order alone decides which grant wins, so a move can take administration away
|
|
5124
|
+
* without changing the list's contents. It carries the same retention rule as
|
|
5125
|
+
* the two mutation paths.
|
|
5126
|
+
*/
|
|
5127
|
+
function applyMoveGrantAction(document, action) {
|
|
5128
|
+
assertActionInputShape(action.input);
|
|
5129
|
+
const { id, index } = action.input;
|
|
5130
|
+
const { grants, creator } = document.state.auth;
|
|
5131
|
+
const from = grants.findIndex((g) => g.id === id);
|
|
5132
|
+
if (from === -1) throw new GrantNotFoundError(id);
|
|
5133
|
+
const next = [...grants];
|
|
5134
|
+
const [moved] = next.splice(from, 1);
|
|
5135
|
+
const to = Math.max(0, Math.min(index, next.length));
|
|
5136
|
+
next.splice(to, 0, moved);
|
|
5137
|
+
assertAuthAdministrationRetained(creator, grants, next, id);
|
|
5138
|
+
return withGrants(document, next);
|
|
5139
|
+
}
|
|
5140
|
+
/**
|
|
5141
|
+
* Dispatches an auth-scope action to its handler. This is the auth scope's
|
|
5142
|
+
* dedicated reducer: it is applied by the base reducer instead of the model
|
|
5143
|
+
* reducer, mirroring the document-scope platform handlers. Unknown types are a
|
|
5144
|
+
* no-op, matching the model-reducer default.
|
|
5145
|
+
*/
|
|
5146
|
+
function applyAuthAction(document, action) {
|
|
5147
|
+
switch (action.type) {
|
|
5148
|
+
case "INITIALIZE_AUTH": return applyInitializeAuthAction(document, action);
|
|
5149
|
+
case "SET_GRANT": return applySetGrantAction(document, action);
|
|
5150
|
+
case "REMOVE_GRANT": return applyRemoveGrantAction(document, action);
|
|
5151
|
+
case "MOVE_GRANT": return applyMoveGrantAction(document, action);
|
|
5152
|
+
default: return document;
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
/**
|
|
5156
|
+
* Because only creators can initialize auth scopes, we must verify that either
|
|
5157
|
+
* the document has no auth or the version and creator match.
|
|
5158
|
+
*/
|
|
5159
|
+
function assertAuthPreservedOnDuplicate(documentId, source, duplicated) {
|
|
5160
|
+
if (!source || source.version === 0) return;
|
|
5161
|
+
if (duplicated === void 0 || duplicated.version !== source.version || duplicated.creator !== source.creator) throw new AuthPolicyNotPreservedError(documentId);
|
|
5162
|
+
}
|
|
5163
|
+
/**
|
|
5164
|
+
* The auth scope a state snapshot may install, given the policy already there.
|
|
5165
|
+
*
|
|
5166
|
+
* `applyAuthAction` is the validated door onto `state.auth`, but a whole-state
|
|
5167
|
+
* snapshot (UPGRADE_DOCUMENT's `initialState`, LOAD_STATE's `data`) replaces the
|
|
5168
|
+
* scope wholesale and is authorized as a `document`-scope write. Without this,
|
|
5169
|
+
* a subject holding `execute` on `document` and no auth grant at all can install
|
|
5170
|
+
* a policy of its choosing, name itself `creator` (which exempts the policy from
|
|
5171
|
+
* the retention rule for good), or wipe an existing policy by carrying the
|
|
5172
|
+
* default uninitialized one.
|
|
5173
|
+
*
|
|
5174
|
+
* Three cases:
|
|
5175
|
+
*
|
|
5176
|
+
* - the snapshot carries no policy, or an uninitialized one: the document
|
|
5177
|
+
* keeps the policy it has, so a default-state upgrade cannot reset it;
|
|
5178
|
+
* - the document is uninitialized and the snapshot carries a policy: the
|
|
5179
|
+
* policy is installed after the same validation genesis applies, so it
|
|
5180
|
+
* cannot be born locked out;
|
|
5181
|
+
* - both carry a policy: they must agree. A duplicate preserves its source's
|
|
5182
|
+
* policy (see {@link assertAuthPreservedOnDuplicate}), and anything else is
|
|
5183
|
+
* an attempt to replace one policy with another.
|
|
5184
|
+
*/
|
|
5185
|
+
function resolveSnapshotAuth(documentId, documentType, current, incoming) {
|
|
5186
|
+
const currentAuth = current ?? createAuthState({
|
|
5187
|
+
version: 0,
|
|
5188
|
+
grants: []
|
|
5189
|
+
});
|
|
5190
|
+
if (!incoming || !incoming.version) return currentAuth;
|
|
5191
|
+
if (currentAuth.version !== 0) {
|
|
5192
|
+
if (stringify$1(incoming) !== stringify$1(currentAuth)) throw new AuthPolicyNotPreservedError(documentId);
|
|
5193
|
+
return incoming;
|
|
5194
|
+
}
|
|
5195
|
+
if (!Array.isArray(incoming.grants)) throw new InvalidActionInputError({ grants: "must be an array" });
|
|
5196
|
+
assertValidInitialGrants(incoming.grants, documentType, incoming.creator);
|
|
5197
|
+
return incoming;
|
|
5198
|
+
}
|
|
5199
|
+
/** UNDO, REDO and PRUNE are rejected on the auth scope. */
|
|
5200
|
+
function assertAuthScopeActionAllowed(action) {
|
|
5201
|
+
if (action.scope === "auth" && [
|
|
5202
|
+
"UNDO",
|
|
5203
|
+
"REDO",
|
|
5204
|
+
"PRUNE"
|
|
5205
|
+
].includes(action.type)) throw new AuthActionNotAllowedError(action.type);
|
|
5206
|
+
}
|
|
5207
|
+
/**
|
|
5208
|
+
* True iff authorization rejected the action.
|
|
5209
|
+
*/
|
|
5210
|
+
function isDenied(operation) {
|
|
5211
|
+
return operation.deniedReason !== void 0;
|
|
5212
|
+
}
|
|
4311
5213
|
const BaseDocumentHeaderSchema = object({
|
|
4312
5214
|
id: string(),
|
|
4313
5215
|
name: string(),
|
|
@@ -4438,6 +5340,7 @@ function createDocumentScopeOperations(header, state) {
|
|
|
4438
5340
|
function baseCreateDocument(createState, initialState, documentType = "") {
|
|
4439
5341
|
const state = createState(initialState);
|
|
4440
5342
|
const header = createPresignedHeader(generateId(), documentType);
|
|
5343
|
+
header.protocolVersions = { "base-reducer": 2 };
|
|
4441
5344
|
return {
|
|
4442
5345
|
header,
|
|
4443
5346
|
state,
|
|
@@ -4461,6 +5364,18 @@ function hashDocumentStateForScope(document, scope = "global") {
|
|
|
4461
5364
|
* Uses the V2 model where consecutive NOOPs form chains.
|
|
4462
5365
|
* Unlike V1 garbageCollect, this preserves ALL operations but marks which to apply.
|
|
4463
5366
|
*/
|
|
5367
|
+
/**
|
|
5368
|
+
* The base-reducer protocol version a document is written against. Every
|
|
5369
|
+
* document carries one, set from the CREATE_DOCUMENT input. A header without
|
|
5370
|
+
* one was built outside that path, and choosing a version on its behalf would
|
|
5371
|
+
* replay the document through a reducer it was not written for, so it is an
|
|
5372
|
+
* error rather than a default.
|
|
5373
|
+
*/
|
|
5374
|
+
function baseReducerVersion(header) {
|
|
5375
|
+
const version = header.protocolVersions?.["base-reducer"];
|
|
5376
|
+
if (typeof version !== "number") throw new Error(`Document ${header.id} carries no base-reducer protocol version`);
|
|
5377
|
+
return version;
|
|
5378
|
+
}
|
|
4464
5379
|
function garbageCollectV2(sortedOperations) {
|
|
4465
5380
|
const result = [];
|
|
4466
5381
|
let noopChainLength = 0;
|
|
@@ -4477,9 +5392,26 @@ function garbageCollectV2(sortedOperations) {
|
|
|
4477
5392
|
const defaultCreateState = (state) => {
|
|
4478
5393
|
return state;
|
|
4479
5394
|
};
|
|
5395
|
+
/**
|
|
5396
|
+
* Records an operation in the history without applying it, which is what a
|
|
5397
|
+
* denied operation needs: it occupies its index and contributes no state.
|
|
5398
|
+
*
|
|
5399
|
+
* The scope defaults to the action's own, and is passed explicitly by a rebuild
|
|
5400
|
+
* that is walking one stream and does not want to trust the action's copy.
|
|
5401
|
+
*/
|
|
5402
|
+
function appendWithoutApplying(document, operation, scope = operation.action.scope) {
|
|
5403
|
+
return {
|
|
5404
|
+
...document,
|
|
5405
|
+
operations: {
|
|
5406
|
+
...document.operations,
|
|
5407
|
+
[scope]: [...document.operations[scope] ?? [], operation]
|
|
5408
|
+
}
|
|
5409
|
+
};
|
|
5410
|
+
}
|
|
4480
5411
|
function replayDocument(initialState, operations, reducer, header, dispatch, skipHeaderOperations = {}, options) {
|
|
4481
5412
|
const { checkHashes = true, reuseOperationResultingState, operationResultingStateParser = parseResultingState, skipIndexValidation } = options || {};
|
|
4482
|
-
|
|
5413
|
+
const backfilledInitialState = backfillAuthState(initialState);
|
|
5414
|
+
let documentState = backfilledInitialState;
|
|
4483
5415
|
const operationsToReplay = [];
|
|
4484
5416
|
const allScopes = new Set([
|
|
4485
5417
|
...Object.keys(operations),
|
|
@@ -4514,12 +5446,13 @@ function replayDocument(initialState, operations, reducer, header, dispatch, ski
|
|
|
4514
5446
|
const document = {
|
|
4515
5447
|
header,
|
|
4516
5448
|
state: defaultCreateState(documentState),
|
|
4517
|
-
initialState,
|
|
5449
|
+
initialState: backfilledInitialState,
|
|
4518
5450
|
operations: initialOperations,
|
|
4519
5451
|
clipboard: []
|
|
4520
5452
|
};
|
|
4521
5453
|
let result = document;
|
|
4522
5454
|
if (operationsToReplay.length) result = operationsToReplay.reduce((document, operation) => {
|
|
5455
|
+
if (isDenied(operation)) return updateHeaderRevision(appendWithoutApplying(document, operation), operation.action.scope, operation.timestampUtcMs);
|
|
4523
5456
|
return reducer(document, operation.action, dispatch, {
|
|
4524
5457
|
ignoreSkipOperations: true,
|
|
4525
5458
|
checkHashes,
|
|
@@ -4801,13 +5734,15 @@ function redoOperation(document, action, skip) {
|
|
|
4801
5734
|
});
|
|
4802
5735
|
}
|
|
4803
5736
|
function loadStateOperation(document, action) {
|
|
5737
|
+
const loaded = backfillAuthState(action.state.data);
|
|
5738
|
+
loaded.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, loaded.auth);
|
|
4804
5739
|
return {
|
|
4805
5740
|
...document,
|
|
4806
5741
|
header: {
|
|
4807
5742
|
...document.header,
|
|
4808
5743
|
name: action.state.name
|
|
4809
5744
|
},
|
|
4810
|
-
state:
|
|
5745
|
+
state: loaded
|
|
4811
5746
|
};
|
|
4812
5747
|
}
|
|
4813
5748
|
function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
|
|
@@ -4981,12 +5916,13 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
|
|
|
4981
5916
|
function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
4982
5917
|
const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main" } = options;
|
|
4983
5918
|
let _action = actionFromAction(action);
|
|
5919
|
+
assertAuthScopeActionAllowed(_action);
|
|
4984
5920
|
let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
|
|
4985
5921
|
let newDocument = { ...document };
|
|
4986
5922
|
let reuseLastOperationIndex = false;
|
|
4987
5923
|
const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
|
|
4988
5924
|
if (isUndoRedo(_action)) {
|
|
4989
|
-
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ??
|
|
5925
|
+
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? baseReducerVersion(document.header));
|
|
4990
5926
|
_action = transformedAction;
|
|
4991
5927
|
skipValue = calculatedSkip;
|
|
4992
5928
|
newDocument = processedDocument;
|
|
@@ -5002,7 +5938,7 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
|
5002
5938
|
branch
|
|
5003
5939
|
};
|
|
5004
5940
|
newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
|
|
5005
|
-
const protocolVersion = options.protocolVersion ??
|
|
5941
|
+
const protocolVersion = options.protocolVersion ?? baseReducerVersion(document.header);
|
|
5006
5942
|
if (isUndo(action) && protocolVersion < 2) return processUndoOperation(newDocument, action.scope, customReducer);
|
|
5007
5943
|
const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
|
|
5008
5944
|
if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
|
|
@@ -5028,6 +5964,13 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
|
5028
5964
|
}
|
|
5029
5965
|
newDocument = create(newDocument, (draft) => {
|
|
5030
5966
|
try {
|
|
5967
|
+
if (_action.scope === "auth") {
|
|
5968
|
+
const authState = applyAuthAction(newDocument, _action).state;
|
|
5969
|
+
unsafe(() => {
|
|
5970
|
+
draft.state = castDraft(authState);
|
|
5971
|
+
});
|
|
5972
|
+
return;
|
|
5973
|
+
}
|
|
5031
5974
|
const newState = customReducer(draft.state, _action, dispatch);
|
|
5032
5975
|
if (newState) unsafe(() => {
|
|
5033
5976
|
draft.state = castDraft(newState);
|
|
@@ -5057,8 +6000,8 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
|
5057
6000
|
"PRUNE"
|
|
5058
6001
|
].includes(_action.type)) return newDocument;
|
|
5059
6002
|
const scope = _action.scope || "global";
|
|
5060
|
-
|
|
5061
|
-
|
|
6003
|
+
const replayHash = options.replayOptions?.operation.hash;
|
|
6004
|
+
const hash = replayHash ? replayHash : hashDocumentStateForScope(newDocument, scope);
|
|
5062
6005
|
const lastOperation = newDocument.operations[scope]?.at(-1);
|
|
5063
6006
|
if (lastOperation) {
|
|
5064
6007
|
lastOperation.hash = hash;
|
|
@@ -5817,14 +6760,27 @@ const documentModelStateReducer = (state, action) => {
|
|
|
5817
6760
|
}
|
|
5818
6761
|
};
|
|
5819
6762
|
const documentModelReducer = createReducer(documentModelStateReducer);
|
|
6763
|
+
/**
|
|
6764
|
+
* Canonical document-model version normalization: documents stamped with 0
|
|
6765
|
+
* or nothing at all predate versioning and are treated as version 1, the
|
|
6766
|
+
* same version the registry assigns unversioned modules. Every consumer
|
|
6767
|
+
* that resolves a module or compares versions must use this rule; resolving
|
|
6768
|
+
* 0 to "latest" instead re-pins a legacy document's history to whichever
|
|
6769
|
+
* module happens to be newest.
|
|
6770
|
+
*/
|
|
6771
|
+
function normalizeDocumentModelVersion(version) {
|
|
6772
|
+
return version && version > 0 ? version : 1;
|
|
6773
|
+
}
|
|
5820
6774
|
function applyInitialState(document, action) {
|
|
5821
6775
|
const input = action.input;
|
|
5822
6776
|
const newState = input.initialState || input.state;
|
|
5823
6777
|
if (newState) {
|
|
5824
|
-
|
|
6778
|
+
const merged = backfillAuthState({
|
|
5825
6779
|
...document.state,
|
|
5826
6780
|
...newState
|
|
5827
|
-
};
|
|
6781
|
+
});
|
|
6782
|
+
merged.auth = resolveSnapshotAuth(document.header.id, document.header.documentType, backfillAuthState({ ...document.state }).auth, merged.auth);
|
|
6783
|
+
document.state = merged;
|
|
5828
6784
|
document.initialState = document.state;
|
|
5829
6785
|
}
|
|
5830
6786
|
}
|
|
@@ -5884,7 +6840,6 @@ function computeUpgradeTransitions(manifest, fromVersion, toVersion) {
|
|
|
5884
6840
|
}
|
|
5885
6841
|
return transitions;
|
|
5886
6842
|
}
|
|
5887
|
-
const NON_DOMAIN_SCOPES$1 = new Set(["auth", "document"]);
|
|
5888
6843
|
function highestReducerVersion(reducers) {
|
|
5889
6844
|
const keys = Object.keys(reducers).map(Number);
|
|
5890
6845
|
if (keys.length === 0) throw new Error("VersionedReplayConfig.reducers must not be empty");
|
|
@@ -5912,15 +6867,15 @@ function highestReducerVersion(reducers) {
|
|
|
5912
6867
|
*/
|
|
5913
6868
|
function replayDocumentVersioned(initialState, operations, config, header, dispatch, options) {
|
|
5914
6869
|
const { checkHashes = true, skipIndexValidation } = options || {};
|
|
5915
|
-
const protocolVersion = header
|
|
6870
|
+
const protocolVersion = baseReducerVersion(header);
|
|
5916
6871
|
const spine = (operations["document"] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
5917
6872
|
const upgrades = spine.filter((op) => op.action.type === "UPGRADE_DOCUMENT");
|
|
5918
6873
|
const legacyFallback = () => {
|
|
5919
|
-
const
|
|
6874
|
+
const replayOps = Object.fromEntries(Object.entries(operations).filter(([s]) => s !== "document"));
|
|
5920
6875
|
const latestVersion = highestReducerVersion(config.reducers);
|
|
5921
6876
|
const reducer = config.reducers[latestVersion];
|
|
5922
6877
|
return {
|
|
5923
|
-
...replayDocument(initialState,
|
|
6878
|
+
...replayDocument(initialState, replayOps, reducer, header, dispatch, {}, options),
|
|
5924
6879
|
operations
|
|
5925
6880
|
};
|
|
5926
6881
|
};
|
|
@@ -5944,14 +6899,14 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5944
6899
|
const a = op.action;
|
|
5945
6900
|
return a.input.fromVersion > 0 && a.input.fromVersion < a.input.toVersion;
|
|
5946
6901
|
});
|
|
5947
|
-
const
|
|
6902
|
+
const replayScopes = Object.keys(operations).filter((s) => s !== "document");
|
|
5948
6903
|
const scopeOps = {};
|
|
5949
|
-
for (const s of
|
|
6904
|
+
for (const s of replayScopes) scopeOps[s] = (operations[s] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
5950
6905
|
const boundaries = validatedUpgrades.map((upgradeOp) => {
|
|
5951
6906
|
const revisionSnapshot = upgradeOp.action.input.revision;
|
|
5952
6907
|
const upgradeTimestamp = upgradeOp.timestampUtcMs;
|
|
5953
6908
|
const boundary = {};
|
|
5954
|
-
for (const s of
|
|
6909
|
+
for (const s of replayScopes) {
|
|
5955
6910
|
const ops = scopeOps[s] ?? [];
|
|
5956
6911
|
if (revisionSnapshot !== void 0) {
|
|
5957
6912
|
const rev = revisionSnapshot[s] ?? 0;
|
|
@@ -5969,7 +6924,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5969
6924
|
}
|
|
5970
6925
|
return boundary;
|
|
5971
6926
|
});
|
|
5972
|
-
for (let i = 1; i < boundaries.length; i++) for (const s of
|
|
6927
|
+
for (let i = 1; i < boundaries.length; i++) for (const s of replayScopes) {
|
|
5973
6928
|
const prev = boundaries[i - 1]?.[s] ?? 0;
|
|
5974
6929
|
const curr = boundaries[i]?.[s] ?? 0;
|
|
5975
6930
|
if (boundaries[i]) boundaries[i][s] = Math.max(prev, curr);
|
|
@@ -5981,10 +6936,11 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5981
6936
|
]);
|
|
5982
6937
|
const initialOperations = {};
|
|
5983
6938
|
for (const s of allScopes) initialOperations[s] = [];
|
|
6939
|
+
const backfilledSeed = backfillAuthState(seedState);
|
|
5984
6940
|
let document = {
|
|
5985
6941
|
header,
|
|
5986
|
-
state:
|
|
5987
|
-
initialState:
|
|
6942
|
+
state: backfilledSeed,
|
|
6943
|
+
initialState: backfilledSeed,
|
|
5988
6944
|
operations: initialOperations,
|
|
5989
6945
|
clipboard: []
|
|
5990
6946
|
};
|
|
@@ -5992,17 +6948,15 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
5992
6948
|
const segmentEndHashPerScope = /* @__PURE__ */ new Map();
|
|
5993
6949
|
for (let k = 0; k <= validatedUpgrades.length; k++) {
|
|
5994
6950
|
const reducer = config.reducers[currentVersion];
|
|
5995
|
-
if (!reducer)
|
|
5996
|
-
|
|
5997
|
-
throw new Error(`No reducer registered for document version ${currentVersion}. Available versions: ${available}`);
|
|
5998
|
-
}
|
|
5999
|
-
for (const s of domainScopes) {
|
|
6951
|
+
if (!reducer) throw new UnsupportedDocumentModelVersionError(header.documentType, currentVersion, Object.keys(config.reducers).map(Number).sort((a, b) => a - b));
|
|
6952
|
+
for (const s of replayScopes) {
|
|
6000
6953
|
const ops = scopeOps[s] ?? [];
|
|
6001
6954
|
const segStart = k === 0 ? 0 : boundaries[k - 1]?.[s] ?? 0;
|
|
6002
6955
|
const segEnd = k < validatedUpgrades.length ? boundaries[k]?.[s] ?? ops.length : ops.length;
|
|
6003
6956
|
const segOps = ops.slice(segStart, segEnd);
|
|
6004
6957
|
for (const op of segOps) {
|
|
6005
|
-
document =
|
|
6958
|
+
if (isDenied(op)) document = updateHeaderRevision(appendWithoutApplying(document, op, s), s, op.timestampUtcMs);
|
|
6959
|
+
else document = reducer(document, op.action, dispatch, {
|
|
6006
6960
|
ignoreSkipOperations: true,
|
|
6007
6961
|
checkHashes,
|
|
6008
6962
|
skipIndexValidation,
|
|
@@ -6019,6 +6973,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6019
6973
|
if (!spineOp) continue;
|
|
6020
6974
|
const spineActionType = spineOp.action.type;
|
|
6021
6975
|
if (spineActionType === "CREATE_DOCUMENT" || spineActionType === "UPGRADE_DOCUMENT") continue;
|
|
6976
|
+
if (isDenied(spineOp)) continue;
|
|
6022
6977
|
if (spineActionType === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, spineOp.action);
|
|
6023
6978
|
else document = reducer(document, spineOp.action, dispatch, {
|
|
6024
6979
|
ignoreSkipOperations: true,
|
|
@@ -6049,7 +7004,7 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6049
7004
|
}
|
|
6050
7005
|
};
|
|
6051
7006
|
if (!checkHashes) {
|
|
6052
|
-
const allReplayedOps =
|
|
7007
|
+
const allReplayedOps = replayScopes.flatMap((s) => scopeOps[s] ?? []);
|
|
6053
7008
|
for (const scope of Object.keys(document.state)) {
|
|
6054
7009
|
const capturedHash = segmentEndHashPerScope.get(scope);
|
|
6055
7010
|
const scopeHash = capturedHash !== void 0 ? capturedHash : hashDocumentStateForScope(document, scope);
|
|
@@ -6091,7 +7046,6 @@ function replayDocumentVersioned(initialState, operations, config, header, dispa
|
|
|
6091
7046
|
}
|
|
6092
7047
|
};
|
|
6093
7048
|
}
|
|
6094
|
-
const NON_DOMAIN_SCOPES = new Set(["auth", "document"]);
|
|
6095
7049
|
function zipAsync(data) {
|
|
6096
7050
|
return new Promise((resolve, reject) => {
|
|
6097
7051
|
zip(data, (err, out) => err ? reject(err) : resolve(out));
|
|
@@ -6170,7 +7124,7 @@ async function parseZipData(data) {
|
|
|
6170
7124
|
async function loadFromZipData(data, reducer, options) {
|
|
6171
7125
|
const { initialState, header, clearedOperations } = await parseZipData(data);
|
|
6172
7126
|
return {
|
|
6173
|
-
...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) =>
|
|
7127
|
+
...replayDocument(initialState, Object.fromEntries(Object.entries(clearedOperations).filter(([scope]) => scope !== "document")), reducer, header, void 0, {}, options),
|
|
6174
7128
|
operations: clearedOperations
|
|
6175
7129
|
};
|
|
6176
7130
|
}
|
|
@@ -6193,96 +7147,6 @@ const documentModelLoadFromInput = (input) => {
|
|
|
6193
7147
|
const documentModelSaveToFileHandle = (document, input) => {
|
|
6194
7148
|
return baseSaveToFileHandle(document, input);
|
|
6195
7149
|
};
|
|
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
7150
|
//#endregion
|
|
6287
7151
|
//#region ../document-model/dist/index.js
|
|
6288
7152
|
const isStateOfType$1 = (state) => {
|
|
@@ -7361,6 +8225,6 @@ const driveDocumentModelModule = {
|
|
|
7361
8225
|
}
|
|
7362
8226
|
};
|
|
7363
8227
|
//#endregion
|
|
7364
|
-
export {
|
|
8228
|
+
export { setOperationDescription as $, createState as A, normalizeDocumentModelVersion as B, baseLoadFromInputVersioned as C, createBaseState as D, createAction as E, deleteOperationError as F, setAuthorWebsite as G, releaseNewVersion as H, deserializeSignature as I, setModelExtension as J, setInitialState as K, documentModelDocumentType as L, defaultBaseState as M, deleteModule as N, createPresignedHeader as O, deleteOperation as P, setName as Q, hashDocumentStateForScope as R, baseLoadFromInput as S, buildSignedAction as T, replayDocumentVersioned as U, redo as V, setAuthorName as W, setModelName as X, setModelId as Y, setModuleName as Z, addOperation as _, handleTargetNameCollisions as a, undo as at, baseActions as b, isValidName as c, validateStateSchemaName as ct, documentModelDocumentModelModule as d, setOperationErrorName as et, logger as f, addModule as g, UnsupportedDocumentModelVersionError as h, generateNodesCopy as i, toTransportAction as it, createZip as j, createReducer as k, moveNode as l, generateId as lt, BaseDocumentStateSchema as m, copyNode as n, setOperationSchema as nt, isFileNode as o, validateInitialState as ot, BaseDocumentHeaderSchema as p, setModelDescription as q, driveDocumentModelModule as r, setStateSchema as rt, isFolderNode as s, validateModules as st, addFolder as t, setOperationName as tt, updateNode as u, addOperationError as v, baseSaveToFileHandle as w, baseCreateDocument as x, assertAuthPreservedOnDuplicate as y, isDocumentAction as z };
|
|
7365
8229
|
|
|
7366
|
-
//# sourceMappingURL=document-drive-
|
|
8230
|
+
//# sourceMappingURL=document-drive-BVYCdu_5.js.map
|