@peerbit/document 13.0.44 → 13.1.1
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/benchmark/document-put.js +1224 -63
- package/dist/benchmark/document-put.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/native-rust.d.ts +50 -0
- package/dist/src/native-rust.d.ts.map +1 -0
- package/dist/src/native-rust.js +38 -0
- package/dist/src/native-rust.js.map +1 -0
- package/dist/src/policy.d.ts +51 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +347 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/program.d.ts +139 -22
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +3074 -342
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +80 -6
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +1155 -59
- package/dist/src/search.js.map +1 -1
- package/dist/src/transform.d.ts +42 -0
- package/dist/src/transform.d.ts.map +1 -0
- package/dist/src/transform.js +136 -0
- package/dist/src/transform.js.map +1 -0
- package/package.json +21 -16
- package/src/index.ts +2 -0
- package/src/native-rust.ts +152 -0
- package/src/policy.ts +592 -0
- package/src/program.ts +5020 -467
- package/src/search.ts +1766 -109
- package/src/transform.ts +277 -0
package/src/policy.ts
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import type { PublicSignKey } from "@peerbit/crypto";
|
|
2
|
+
import type { Entry } from "@peerbit/log";
|
|
3
|
+
import type { DeleteOperation, Operation, PutOperation } from "./operation.js";
|
|
4
|
+
import type { CanPerform, CanPerformOperations } from "./program.js";
|
|
5
|
+
|
|
6
|
+
export type CanPerformPolicyDescriptor =
|
|
7
|
+
| {
|
|
8
|
+
readonly kind: "allowAll";
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
readonly kind: "and";
|
|
12
|
+
readonly policies: readonly CanPerformPolicyDescriptor[];
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
readonly kind: "or";
|
|
16
|
+
readonly policies: readonly CanPerformPolicyDescriptor[];
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
readonly kind: "put";
|
|
20
|
+
readonly policy: CanPerformPolicyDescriptor;
|
|
21
|
+
}
|
|
22
|
+
| {
|
|
23
|
+
readonly kind: "delete";
|
|
24
|
+
readonly policy: CanPerformPolicyDescriptor;
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
readonly kind: "signedByPublicKey";
|
|
28
|
+
readonly publicKey: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
readonly kind: "signedByField";
|
|
32
|
+
readonly path: string | readonly string[];
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
readonly kind: "deleteSignedByExistingField";
|
|
36
|
+
readonly path: string | readonly string[];
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
readonly kind: "sameSignersAsPrevious";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const NATIVE_CAN_PERFORM_POLICY = Symbol.for(
|
|
43
|
+
"@peerbit/document/native-can-perform-policy",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const copyBytes = (bytes: Uint8Array): Uint8Array => new Uint8Array(bytes);
|
|
47
|
+
|
|
48
|
+
const bytesEqual = (left: Uint8Array, right: Uint8Array): boolean => {
|
|
49
|
+
if (left.byteLength !== right.byteLength) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
for (let i = 0; i < left.byteLength; i++) {
|
|
53
|
+
if (left[i] !== right[i]) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const asPath = (path: string | readonly string[]): readonly string[] =>
|
|
61
|
+
typeof path === "string" ? [path] : path;
|
|
62
|
+
|
|
63
|
+
type FieldValueAccessor = (value: unknown) => unknown;
|
|
64
|
+
|
|
65
|
+
const createFieldValueAccessor = (
|
|
66
|
+
path: string | readonly string[],
|
|
67
|
+
): FieldValueAccessor => {
|
|
68
|
+
const segments = asPath(path);
|
|
69
|
+
if (segments.length === 1) {
|
|
70
|
+
const segment = segments[0]!;
|
|
71
|
+
return (value: unknown) =>
|
|
72
|
+
(value as Record<string, unknown> | undefined)?.[segment];
|
|
73
|
+
}
|
|
74
|
+
return (value: unknown) => {
|
|
75
|
+
let current = value as Record<string, unknown> | undefined;
|
|
76
|
+
for (const segment of segments) {
|
|
77
|
+
if (current == null) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
current = current[segment] as Record<string, unknown> | undefined;
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const keyRawBytes = (publicKey: PublicSignKey): Uint8Array | undefined =>
|
|
87
|
+
(publicKey as PublicSignKey & { publicKey?: Uint8Array }).publicKey;
|
|
88
|
+
|
|
89
|
+
const valueMatchesPublicKeyBytes = (
|
|
90
|
+
value: unknown,
|
|
91
|
+
publicKey: PublicSignKey,
|
|
92
|
+
publicKeyBytes: Uint8Array,
|
|
93
|
+
rawPublicKeyBytes?: Uint8Array,
|
|
94
|
+
): boolean => {
|
|
95
|
+
if (!value) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (value instanceof Uint8Array) {
|
|
99
|
+
return (
|
|
100
|
+
bytesEqual(value, publicKeyBytes) ||
|
|
101
|
+
(rawPublicKeyBytes ? bytesEqual(value, rawPublicKeyBytes) : false)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const maybePublicKey = value as Partial<PublicSignKey> & {
|
|
105
|
+
publicKey?: Uint8Array;
|
|
106
|
+
};
|
|
107
|
+
if (typeof maybePublicKey.equals === "function") {
|
|
108
|
+
return maybePublicKey.equals(publicKey);
|
|
109
|
+
}
|
|
110
|
+
if (maybePublicKey.bytes instanceof Uint8Array) {
|
|
111
|
+
return bytesEqual(maybePublicKey.bytes, publicKeyBytes);
|
|
112
|
+
}
|
|
113
|
+
if (maybePublicKey.publicKey instanceof Uint8Array) {
|
|
114
|
+
return (
|
|
115
|
+
!!rawPublicKeyBytes &&
|
|
116
|
+
bytesEqual(maybePublicKey.publicKey, rawPublicKeyBytes)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const valueMatchesPublicKey = (value: unknown, publicKey: PublicSignKey): boolean =>
|
|
123
|
+
valueMatchesPublicKeyBytes(
|
|
124
|
+
value,
|
|
125
|
+
publicKey,
|
|
126
|
+
publicKey.bytes,
|
|
127
|
+
keyRawBytes(publicKey),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const createPublicKeyValueMatcher = (
|
|
131
|
+
publicKey: PublicSignKey,
|
|
132
|
+
): ((value: unknown) => boolean) => {
|
|
133
|
+
const publicKeyBytes = publicKey.bytes;
|
|
134
|
+
const rawPublicKeyBytes = keyRawBytes(publicKey);
|
|
135
|
+
return (value) =>
|
|
136
|
+
valueMatchesPublicKeyBytes(
|
|
137
|
+
value,
|
|
138
|
+
publicKey,
|
|
139
|
+
publicKeyBytes,
|
|
140
|
+
rawPublicKeyBytes,
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const valueMatchesAnyPublicKey = (
|
|
145
|
+
value: unknown,
|
|
146
|
+
publicKeys: readonly PublicSignKey[],
|
|
147
|
+
): boolean => {
|
|
148
|
+
if (!value) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
for (const publicKey of publicKeys) {
|
|
152
|
+
if (valueMatchesPublicKey(value, publicKey)) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const allDescriptors = <T>(
|
|
160
|
+
policies: readonly CanPerform<T>[],
|
|
161
|
+
): CanPerformPolicyDescriptor[] | undefined => {
|
|
162
|
+
const descriptors: CanPerformPolicyDescriptor[] = [];
|
|
163
|
+
for (const policy of policies) {
|
|
164
|
+
const descriptor = getCanPerformPolicyDescriptor(policy);
|
|
165
|
+
if (!descriptor) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
descriptors.push(descriptor);
|
|
169
|
+
}
|
|
170
|
+
return descriptors;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
type DescribedCanPerformFunction<T> = CanPerform<T> & {
|
|
174
|
+
readonly [NATIVE_CAN_PERFORM_POLICY]?: CanPerformPolicyDescriptor;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const attachCanPerformPolicyDescriptor = <T>(
|
|
178
|
+
fn: CanPerform<T>,
|
|
179
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
180
|
+
): DescribedCanPerformFunction<T> => {
|
|
181
|
+
if ((fn as DescribedCanPerformFunction<T>)[NATIVE_CAN_PERFORM_POLICY]) {
|
|
182
|
+
return fn as DescribedCanPerformFunction<T>;
|
|
183
|
+
}
|
|
184
|
+
Object.defineProperty(fn, NATIVE_CAN_PERFORM_POLICY, {
|
|
185
|
+
value: Object.freeze(descriptor),
|
|
186
|
+
enumerable: false,
|
|
187
|
+
writable: false,
|
|
188
|
+
configurable: false,
|
|
189
|
+
});
|
|
190
|
+
return fn as DescribedCanPerformFunction<T>;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const getCanPerformPolicyDescriptor = <T>(
|
|
194
|
+
canPerform: CanPerform<T> | undefined,
|
|
195
|
+
): CanPerformPolicyDescriptor | undefined =>
|
|
196
|
+
(canPerform as DescribedCanPerformFunction<T> | undefined)?.[
|
|
197
|
+
NATIVE_CAN_PERFORM_POLICY
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
export type CanPerformPolicyEvaluator = (
|
|
201
|
+
document: unknown,
|
|
202
|
+
) => boolean;
|
|
203
|
+
type CanPerformDeletePolicyEvaluator = (
|
|
204
|
+
existingDocument: unknown,
|
|
205
|
+
) => boolean;
|
|
206
|
+
|
|
207
|
+
const allowFastPath = (): boolean => true;
|
|
208
|
+
const denyFastPath = (): boolean => false;
|
|
209
|
+
|
|
210
|
+
export const createCanPerformPolicyEvaluator = (
|
|
211
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
212
|
+
localPublicKey: PublicSignKey | undefined,
|
|
213
|
+
): CanPerformPolicyEvaluator => {
|
|
214
|
+
switch (descriptor.kind) {
|
|
215
|
+
case "allowAll":
|
|
216
|
+
return allowFastPath;
|
|
217
|
+
case "signedByPublicKey":
|
|
218
|
+
return localPublicKey &&
|
|
219
|
+
bytesEqual(descriptor.publicKey, localPublicKey.bytes)
|
|
220
|
+
? allowFastPath
|
|
221
|
+
: denyFastPath;
|
|
222
|
+
case "put":
|
|
223
|
+
return createCanPerformPolicyEvaluator(
|
|
224
|
+
descriptor.policy,
|
|
225
|
+
localPublicKey,
|
|
226
|
+
);
|
|
227
|
+
case "delete":
|
|
228
|
+
return denyFastPath;
|
|
229
|
+
case "deleteSignedByExistingField":
|
|
230
|
+
case "sameSignersAsPrevious":
|
|
231
|
+
return denyFastPath;
|
|
232
|
+
case "and":
|
|
233
|
+
return descriptor.policies
|
|
234
|
+
.map((policy) =>
|
|
235
|
+
createCanPerformPolicyEvaluator(
|
|
236
|
+
policy,
|
|
237
|
+
localPublicKey,
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
.reduce<CanPerformPolicyEvaluator>(
|
|
241
|
+
(previous, next) => (document) =>
|
|
242
|
+
previous(document) && next(document),
|
|
243
|
+
allowFastPath,
|
|
244
|
+
);
|
|
245
|
+
case "or":
|
|
246
|
+
return descriptor.policies
|
|
247
|
+
.map((policy) =>
|
|
248
|
+
createCanPerformPolicyEvaluator(
|
|
249
|
+
policy,
|
|
250
|
+
localPublicKey,
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
.reduce<CanPerformPolicyEvaluator>(
|
|
254
|
+
(previous, next) => (document) =>
|
|
255
|
+
previous(document) || next(document),
|
|
256
|
+
denyFastPath,
|
|
257
|
+
);
|
|
258
|
+
case "signedByField": {
|
|
259
|
+
if (!localPublicKey) {
|
|
260
|
+
return denyFastPath;
|
|
261
|
+
}
|
|
262
|
+
const getFieldValue = createFieldValueAccessor(descriptor.path);
|
|
263
|
+
const matchesLocalPublicKey =
|
|
264
|
+
createPublicKeyValueMatcher(localPublicKey);
|
|
265
|
+
return (document) => matchesLocalPublicKey(getFieldValue(document));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export const createCanPerformDeletePolicyEvaluator = (
|
|
271
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
272
|
+
localPublicKey: PublicSignKey | undefined,
|
|
273
|
+
): CanPerformDeletePolicyEvaluator => {
|
|
274
|
+
switch (descriptor.kind) {
|
|
275
|
+
case "allowAll":
|
|
276
|
+
return allowFastPath;
|
|
277
|
+
case "signedByPublicKey":
|
|
278
|
+
return localPublicKey &&
|
|
279
|
+
bytesEqual(descriptor.publicKey, localPublicKey.bytes)
|
|
280
|
+
? allowFastPath
|
|
281
|
+
: denyFastPath;
|
|
282
|
+
case "signedByField":
|
|
283
|
+
case "sameSignersAsPrevious":
|
|
284
|
+
case "put":
|
|
285
|
+
return denyFastPath;
|
|
286
|
+
case "delete":
|
|
287
|
+
return createCanPerformDeletePolicyEvaluator(
|
|
288
|
+
descriptor.policy,
|
|
289
|
+
localPublicKey,
|
|
290
|
+
);
|
|
291
|
+
case "deleteSignedByExistingField": {
|
|
292
|
+
if (!localPublicKey) {
|
|
293
|
+
return denyFastPath;
|
|
294
|
+
}
|
|
295
|
+
const getFieldValue = createFieldValueAccessor(descriptor.path);
|
|
296
|
+
const matchesLocalPublicKey =
|
|
297
|
+
createPublicKeyValueMatcher(localPublicKey);
|
|
298
|
+
return (document) => matchesLocalPublicKey(getFieldValue(document));
|
|
299
|
+
}
|
|
300
|
+
case "and":
|
|
301
|
+
return descriptor.policies
|
|
302
|
+
.map((policy) =>
|
|
303
|
+
createCanPerformDeletePolicyEvaluator(policy, localPublicKey),
|
|
304
|
+
)
|
|
305
|
+
.reduce<CanPerformDeletePolicyEvaluator>(
|
|
306
|
+
(previous, next) => (document) =>
|
|
307
|
+
previous(document) && next(document),
|
|
308
|
+
allowFastPath,
|
|
309
|
+
);
|
|
310
|
+
case "or":
|
|
311
|
+
return descriptor.policies
|
|
312
|
+
.map((policy) =>
|
|
313
|
+
createCanPerformDeletePolicyEvaluator(policy, localPublicKey),
|
|
314
|
+
)
|
|
315
|
+
.reduce<CanPerformDeletePolicyEvaluator>(
|
|
316
|
+
(previous, next) => (document) =>
|
|
317
|
+
previous(document) || next(document),
|
|
318
|
+
denyFastPath,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export const canPerformPolicyNeedsDeleteValue = (
|
|
324
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
325
|
+
): boolean => {
|
|
326
|
+
switch (descriptor.kind) {
|
|
327
|
+
case "deleteSignedByExistingField":
|
|
328
|
+
return true;
|
|
329
|
+
case "and":
|
|
330
|
+
case "or":
|
|
331
|
+
return descriptor.policies.some(canPerformPolicyNeedsDeleteValue);
|
|
332
|
+
case "put":
|
|
333
|
+
case "delete":
|
|
334
|
+
return canPerformPolicyNeedsDeleteValue(descriptor.policy);
|
|
335
|
+
default:
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
export const canPerformPolicyDeleteFieldPaths = (
|
|
341
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
342
|
+
): readonly (string | readonly string[])[] => {
|
|
343
|
+
switch (descriptor.kind) {
|
|
344
|
+
case "deleteSignedByExistingField":
|
|
345
|
+
return [descriptor.path];
|
|
346
|
+
case "and":
|
|
347
|
+
case "or":
|
|
348
|
+
return descriptor.policies.flatMap((policy) => [
|
|
349
|
+
...canPerformPolicyDeleteFieldPaths(policy),
|
|
350
|
+
]);
|
|
351
|
+
case "delete":
|
|
352
|
+
return canPerformPolicyDeleteFieldPaths(descriptor.policy);
|
|
353
|
+
default:
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
export const canPerformPolicySignedByFieldPaths = (
|
|
359
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
360
|
+
): readonly (string | readonly string[])[] => {
|
|
361
|
+
switch (descriptor.kind) {
|
|
362
|
+
case "signedByField":
|
|
363
|
+
return [descriptor.path];
|
|
364
|
+
case "and":
|
|
365
|
+
case "or":
|
|
366
|
+
return descriptor.policies.flatMap((policy) => [
|
|
367
|
+
...canPerformPolicySignedByFieldPaths(policy),
|
|
368
|
+
]);
|
|
369
|
+
case "put":
|
|
370
|
+
return canPerformPolicySignedByFieldPaths(descriptor.policy);
|
|
371
|
+
default:
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
export const canPerformPolicyPutNeedsEntryPublicKeys = (
|
|
377
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
378
|
+
): boolean => {
|
|
379
|
+
switch (descriptor.kind) {
|
|
380
|
+
case "signedByPublicKey":
|
|
381
|
+
case "signedByField":
|
|
382
|
+
case "sameSignersAsPrevious":
|
|
383
|
+
return true;
|
|
384
|
+
case "and":
|
|
385
|
+
case "or":
|
|
386
|
+
return descriptor.policies.some(
|
|
387
|
+
canPerformPolicyPutNeedsEntryPublicKeys,
|
|
388
|
+
);
|
|
389
|
+
case "put":
|
|
390
|
+
return canPerformPolicyPutNeedsEntryPublicKeys(descriptor.policy);
|
|
391
|
+
default:
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export const canPerformPolicyNeedsPreviousEntries = (
|
|
397
|
+
descriptor: CanPerformPolicyDescriptor,
|
|
398
|
+
): boolean => {
|
|
399
|
+
switch (descriptor.kind) {
|
|
400
|
+
case "sameSignersAsPrevious":
|
|
401
|
+
return true;
|
|
402
|
+
case "and":
|
|
403
|
+
case "or":
|
|
404
|
+
return descriptor.policies.some(
|
|
405
|
+
canPerformPolicyNeedsPreviousEntries,
|
|
406
|
+
);
|
|
407
|
+
case "put":
|
|
408
|
+
case "delete":
|
|
409
|
+
return canPerformPolicyNeedsPreviousEntries(descriptor.policy);
|
|
410
|
+
default:
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
type CanPerformEntry =
|
|
416
|
+
| Entry<PutOperation>
|
|
417
|
+
| Entry<DeleteOperation>
|
|
418
|
+
| Entry<Operation>;
|
|
419
|
+
|
|
420
|
+
const getEntryPublicKeys = async (
|
|
421
|
+
entry: CanPerformEntry,
|
|
422
|
+
): Promise<PublicSignKey[]> => {
|
|
423
|
+
try {
|
|
424
|
+
if (entry.publicKeys.length > 0) {
|
|
425
|
+
return entry.publicKeys;
|
|
426
|
+
}
|
|
427
|
+
} catch {
|
|
428
|
+
return entry.getPublicKeys();
|
|
429
|
+
}
|
|
430
|
+
return entry.getPublicKeys();
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const entryPublicKeysInclude = async <T>(
|
|
434
|
+
properties: CanPerformOperations<T>,
|
|
435
|
+
publicKey: PublicSignKey,
|
|
436
|
+
): Promise<boolean> => {
|
|
437
|
+
const publicKeys = await getEntryPublicKeys(properties.entry);
|
|
438
|
+
return publicKeys.some((key) => key.equals(publicKey));
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const entryPublicKeysMatchValue = async <T>(
|
|
442
|
+
properties: CanPerformOperations<T>,
|
|
443
|
+
value: unknown,
|
|
444
|
+
): Promise<boolean> => {
|
|
445
|
+
const publicKeys = await getEntryPublicKeys(properties.entry);
|
|
446
|
+
return valueMatchesAnyPublicKey(value, publicKeys);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const publicKeySetsEqual = (
|
|
450
|
+
left: readonly PublicSignKey[],
|
|
451
|
+
right: readonly PublicSignKey[],
|
|
452
|
+
): boolean =>
|
|
453
|
+
left.length === right.length &&
|
|
454
|
+
left.every((leftKey) => right.some((rightKey) => leftKey.equals(rightKey)));
|
|
455
|
+
|
|
456
|
+
const sameSignersAsPrevious = async <T>(
|
|
457
|
+
properties: CanPerformOperations<T>,
|
|
458
|
+
): Promise<boolean> => {
|
|
459
|
+
if (properties.type !== "put") {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
if (!properties.previousEntries?.length) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
const currentPublicKeys = await getEntryPublicKeys(properties.entry);
|
|
466
|
+
for (const previousEntry of properties.previousEntries) {
|
|
467
|
+
const previousPublicKeys = await getEntryPublicKeys(previousEntry);
|
|
468
|
+
if (!publicKeySetsEqual(currentPublicKeys, previousPublicKeys)) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return true;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const andEvaluate = async <T>(
|
|
476
|
+
policies: readonly CanPerform<T>[],
|
|
477
|
+
properties: CanPerformOperations<T>,
|
|
478
|
+
): Promise<boolean> => {
|
|
479
|
+
for (const policy of policies) {
|
|
480
|
+
if (!(await policy(properties))) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return true;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const orEvaluate = async <T>(
|
|
488
|
+
policies: readonly CanPerform<T>[],
|
|
489
|
+
properties: CanPerformOperations<T>,
|
|
490
|
+
): Promise<boolean> => {
|
|
491
|
+
for (const policy of policies) {
|
|
492
|
+
if (await policy(properties)) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const attachIfDescribed = <T>(
|
|
500
|
+
fn: CanPerform<T>,
|
|
501
|
+
descriptor: CanPerformPolicyDescriptor | undefined,
|
|
502
|
+
): CanPerform<T> =>
|
|
503
|
+
descriptor ? attachCanPerformPolicyDescriptor(fn, descriptor) : fn;
|
|
504
|
+
|
|
505
|
+
export const policy = {
|
|
506
|
+
allowAll: <T = unknown>(): CanPerform<T> =>
|
|
507
|
+
attachCanPerformPolicyDescriptor<T>(() => true, { kind: "allowAll" }),
|
|
508
|
+
signedByPublicKey: <T = unknown>(publicKey: PublicSignKey): CanPerform<T> =>
|
|
509
|
+
attachCanPerformPolicyDescriptor<T>(
|
|
510
|
+
(properties) => entryPublicKeysInclude(properties, publicKey),
|
|
511
|
+
{
|
|
512
|
+
kind: "signedByPublicKey",
|
|
513
|
+
publicKey: copyBytes(publicKey.bytes),
|
|
514
|
+
},
|
|
515
|
+
),
|
|
516
|
+
signedByField: <T = unknown>(
|
|
517
|
+
path: string | readonly string[],
|
|
518
|
+
): CanPerform<T> => {
|
|
519
|
+
const getFieldValue = createFieldValueAccessor(path);
|
|
520
|
+
return attachCanPerformPolicyDescriptor<T>(
|
|
521
|
+
async (properties) => {
|
|
522
|
+
if (properties.type !== "put") {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
const fieldValue = getFieldValue(properties.value);
|
|
526
|
+
return entryPublicKeysMatchValue(properties, fieldValue);
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
kind: "signedByField",
|
|
530
|
+
path: typeof path === "string" ? path : Object.freeze([...path]),
|
|
531
|
+
},
|
|
532
|
+
);
|
|
533
|
+
},
|
|
534
|
+
deleteSignedByExistingField: <T = unknown>(
|
|
535
|
+
path: string | readonly string[],
|
|
536
|
+
): CanPerform<T> => {
|
|
537
|
+
const getFieldValue = createFieldValueAccessor(path);
|
|
538
|
+
return attachCanPerformPolicyDescriptor<T>(
|
|
539
|
+
async (properties) => {
|
|
540
|
+
if (properties.type !== "delete" || !properties.value) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
const fieldValue = getFieldValue(properties.value);
|
|
544
|
+
return entryPublicKeysMatchValue(properties, fieldValue);
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
kind: "deleteSignedByExistingField",
|
|
548
|
+
path: typeof path === "string" ? path : Object.freeze([...path]),
|
|
549
|
+
},
|
|
550
|
+
);
|
|
551
|
+
},
|
|
552
|
+
sameSignersAsPrevious: <T = unknown>(): CanPerform<T> =>
|
|
553
|
+
attachCanPerformPolicyDescriptor<T>(sameSignersAsPrevious, {
|
|
554
|
+
kind: "sameSignersAsPrevious",
|
|
555
|
+
}),
|
|
556
|
+
put: <T = unknown>(inner: CanPerform<T>): CanPerform<T> =>
|
|
557
|
+
attachIfDescribed<T>(
|
|
558
|
+
(properties) => properties.type === "put" && inner(properties),
|
|
559
|
+
(() => {
|
|
560
|
+
const descriptor = getCanPerformPolicyDescriptor(inner);
|
|
561
|
+
return descriptor ? { kind: "put", policy: descriptor } : undefined;
|
|
562
|
+
})(),
|
|
563
|
+
),
|
|
564
|
+
delete: <T = unknown>(inner: CanPerform<T>): CanPerform<T> =>
|
|
565
|
+
attachIfDescribed<T>(
|
|
566
|
+
(properties) => properties.type === "delete" && inner(properties),
|
|
567
|
+
(() => {
|
|
568
|
+
const descriptor = getCanPerformPolicyDescriptor(inner);
|
|
569
|
+
return descriptor ? { kind: "delete", policy: descriptor } : undefined;
|
|
570
|
+
})(),
|
|
571
|
+
),
|
|
572
|
+
and: <T = unknown>(...policies: readonly CanPerform<T>[]): CanPerform<T> =>
|
|
573
|
+
attachIfDescribed<T>(
|
|
574
|
+
(properties) => andEvaluate(policies, properties),
|
|
575
|
+
(() => {
|
|
576
|
+
const descriptors = allDescriptors(policies);
|
|
577
|
+
return descriptors
|
|
578
|
+
? { kind: "and", policies: Object.freeze(descriptors.slice()) }
|
|
579
|
+
: undefined;
|
|
580
|
+
})(),
|
|
581
|
+
),
|
|
582
|
+
or: <T = unknown>(...policies: readonly CanPerform<T>[]): CanPerform<T> =>
|
|
583
|
+
attachIfDescribed<T>(
|
|
584
|
+
(properties) => orEvaluate(policies, properties),
|
|
585
|
+
(() => {
|
|
586
|
+
const descriptors = allDescriptors(policies);
|
|
587
|
+
return descriptors
|
|
588
|
+
? { kind: "or", policies: Object.freeze(descriptors.slice()) }
|
|
589
|
+
: undefined;
|
|
590
|
+
})(),
|
|
591
|
+
),
|
|
592
|
+
} as const;
|