@graview/core 0.1.60 → 0.1.62
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/README.md +23 -1
- package/dist/app-places.d.ts.map +1 -1
- package/dist/app-places.js +2 -2
- package/dist/app-places.js.map +1 -1
- package/dist/app.d.ts +10 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js.map +1 -1
- package/dist/arrangement.d.ts +2 -0
- package/dist/arrangement.d.ts.map +1 -1
- package/dist/arrangement.js +14 -0
- package/dist/arrangement.js.map +1 -1
- package/dist/capabilities.js +1 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +2 -1
- package/dist/catalog.js.map +1 -1
- package/dist/cli/check/context.d.ts +5 -0
- package/dist/cli/check/context.d.ts.map +1 -1
- package/dist/cli/check/context.js.map +1 -1
- package/dist/cli/check/face.d.ts +14 -0
- package/dist/cli/check/face.d.ts.map +1 -0
- package/dist/cli/check/face.js +161 -0
- package/dist/cli/check/face.js.map +1 -0
- package/dist/cli/check/hidden-reads.d.ts.map +1 -1
- package/dist/cli/check/hidden-reads.js +26 -0
- package/dist/cli/check/hidden-reads.js.map +1 -1
- package/dist/cli/check/index.d.ts +1 -0
- package/dist/cli/check/index.d.ts.map +1 -1
- package/dist/cli/check/index.js +1 -0
- package/dist/cli/check/index.js.map +1 -1
- package/dist/cli/check/tidying.d.ts +14 -0
- package/dist/cli/check/tidying.d.ts.map +1 -0
- package/dist/cli/check/tidying.js +24 -0
- package/dist/cli/check/tidying.js.map +1 -0
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +4 -4
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/describe.d.ts.map +1 -1
- package/dist/cli/describe.js +14 -1
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/docs.d.ts.map +1 -1
- package/dist/cli/docs.js +5 -3
- package/dist/cli/docs.js.map +1 -1
- package/dist/cli/document-schema.d.ts.map +1 -1
- package/dist/cli/document-schema.js +8 -2
- package/dist/cli/document-schema.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js +2 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/compose.d.ts +14 -0
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +16 -1
- package/dist/compose.js.map +1 -1
- package/dist/css/sanitize.d.ts +125 -0
- package/dist/css/sanitize.d.ts.map +1 -0
- package/dist/css/sanitize.js +989 -0
- package/dist/css/sanitize.js.map +1 -0
- package/dist/css/tables.d.ts +55 -0
- package/dist/css/tables.d.ts.map +1 -0
- package/dist/css/tables.js +137 -0
- package/dist/css/tables.js.map +1 -0
- package/dist/css.d.ts +20 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +19 -0
- package/dist/css.js.map +1 -0
- package/dist/days.d.ts +15 -0
- package/dist/days.d.ts.map +1 -1
- package/dist/days.js +21 -0
- package/dist/days.js.map +1 -1
- package/dist/describe.d.ts +1 -1
- package/dist/describe.d.ts.map +1 -1
- package/dist/document/act-findings.d.ts.map +1 -1
- package/dist/document/act-findings.js +80 -1
- package/dist/document/act-findings.js.map +1 -1
- package/dist/document/blocks.d.ts.map +1 -1
- package/dist/document/blocks.js +2 -1
- package/dist/document/blocks.js.map +1 -1
- package/dist/document/brand-check.d.ts +15 -1
- package/dist/document/brand-check.d.ts.map +1 -1
- package/dist/document/brand-check.js +80 -0
- package/dist/document/brand-check.js.map +1 -1
- package/dist/document/brand.d.ts.map +1 -1
- package/dist/document/brand.js +2 -0
- package/dist/document/brand.js.map +1 -1
- package/dist/document/compile-checked.d.ts.map +1 -1
- package/dist/document/compile-checked.js +4 -3
- package/dist/document/compile-checked.js.map +1 -1
- package/dist/document/compile.d.ts.map +1 -1
- package/dist/document/compile.js +54 -9
- package/dist/document/compile.js.map +1 -1
- package/dist/document/compiled.d.ts +18 -3
- package/dist/document/compiled.d.ts.map +1 -1
- package/dist/document/compiled.js +91 -10
- package/dist/document/compiled.js.map +1 -1
- package/dist/document/connection-findings.d.ts.map +1 -1
- package/dist/document/connection-findings.js +219 -19
- package/dist/document/connection-findings.js.map +1 -1
- package/dist/document/connection-spec.d.ts +7 -0
- package/dist/document/connection-spec.d.ts.map +1 -1
- package/dist/document/connection-spec.js +26 -2
- package/dist/document/connection-spec.js.map +1 -1
- package/dist/document/covers.d.ts +48 -0
- package/dist/document/covers.d.ts.map +1 -0
- package/dist/document/covers.js +90 -0
- package/dist/document/covers.js.map +1 -0
- package/dist/document/describe-place.d.ts +3 -0
- package/dist/document/describe-place.d.ts.map +1 -1
- package/dist/document/describe-place.js +11 -0
- package/dist/document/describe-place.js.map +1 -1
- package/dist/document/described.d.ts +28 -0
- package/dist/document/described.d.ts.map +1 -1
- package/dist/document/diff.js +10 -1
- package/dist/document/diff.js.map +1 -1
- package/dist/document/edit.d.ts +1 -1
- package/dist/document/edit.d.ts.map +1 -1
- package/dist/document/edit.js +186 -18
- package/dist/document/edit.js.map +1 -1
- package/dist/document/expr/evaluate.d.ts +10 -1
- package/dist/document/expr/evaluate.d.ts.map +1 -1
- package/dist/document/expr/evaluate.js +110 -2
- package/dist/document/expr/evaluate.js.map +1 -1
- package/dist/document/expr/index.d.ts +1 -0
- package/dist/document/expr/index.d.ts.map +1 -1
- package/dist/document/expr/index.js +2 -0
- package/dist/document/expr/index.js.map +1 -1
- package/dist/document/expr/typing.d.ts +24 -0
- package/dist/document/expr/typing.d.ts.map +1 -0
- package/dist/document/expr/typing.js +219 -0
- package/dist/document/expr/typing.js.map +1 -0
- package/dist/document/expr/when.d.ts +52 -0
- package/dist/document/expr/when.d.ts.map +1 -0
- package/dist/document/expr/when.js +215 -0
- package/dist/document/expr/when.js.map +1 -0
- package/dist/document/expr-type-findings.d.ts +5 -0
- package/dist/document/expr-type-findings.d.ts.map +1 -0
- package/dist/document/expr-type-findings.js +90 -0
- package/dist/document/expr-type-findings.js.map +1 -0
- package/dist/document/field-spec.d.ts.map +1 -1
- package/dist/document/field-spec.js +5 -0
- package/dist/document/field-spec.js.map +1 -1
- package/dist/document/findings.d.ts +33 -2
- package/dist/document/findings.d.ts.map +1 -1
- package/dist/document/findings.js +14 -17
- package/dist/document/findings.js.map +1 -1
- package/dist/document/index.d.ts +6 -3
- package/dist/document/index.d.ts.map +1 -1
- package/dist/document/index.js +5 -2
- package/dist/document/index.js.map +1 -1
- package/dist/document/linked-findings.js +1 -1
- package/dist/document/linked-findings.js.map +1 -1
- package/dist/document/migrate.d.ts.map +1 -1
- package/dist/document/migrate.js +10 -4
- package/dist/document/migrate.js.map +1 -1
- package/dist/document/rule-line.d.ts +7 -3
- package/dist/document/rule-line.d.ts.map +1 -1
- package/dist/document/rule-line.js +17 -2
- package/dist/document/rule-line.js.map +1 -1
- package/dist/document/rule-reads.d.ts.map +1 -1
- package/dist/document/rule-reads.js +3 -1
- package/dist/document/rule-reads.js.map +1 -1
- package/dist/document/rules.d.ts +30 -0
- package/dist/document/rules.d.ts.map +1 -1
- package/dist/document/rules.js +74 -0
- package/dist/document/rules.js.map +1 -1
- package/dist/document/schema.d.ts +80 -2
- package/dist/document/schema.d.ts.map +1 -1
- package/dist/document/schema.js +90 -4
- package/dist/document/schema.js.map +1 -1
- package/dist/document/to-document.d.ts.map +1 -1
- package/dist/document/to-document.js +3 -1
- package/dist/document/to-document.js.map +1 -1
- package/dist/document.schema.json +304 -5
- package/dist/formats.d.ts +21 -13
- package/dist/formats.d.ts.map +1 -1
- package/dist/formats.js +21 -13
- package/dist/formats.js.map +1 -1
- package/dist/import/coerce.d.ts.map +1 -1
- package/dist/import/coerce.js +16 -4
- package/dist/import/coerce.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/invariants/types.d.ts +7 -2
- package/dist/invariants/types.d.ts.map +1 -1
- package/dist/kind-list.d.ts +1 -0
- package/dist/kind-list.d.ts.map +1 -1
- package/dist/kind-list.js +2 -1
- package/dist/kind-list.js.map +1 -1
- package/dist/lines.d.ts.map +1 -1
- package/dist/lines.js +6 -2
- package/dist/lines.js.map +1 -1
- package/dist/mutations/form.d.ts +2 -0
- package/dist/mutations/form.d.ts.map +1 -1
- package/dist/mutations/form.js +6 -3
- package/dist/mutations/form.js.map +1 -1
- package/dist/mutations/types.d.ts +9 -0
- package/dist/mutations/types.d.ts.map +1 -1
- package/dist/mutations/vote-suggestion.d.ts +67 -0
- package/dist/mutations/vote-suggestion.d.ts.map +1 -0
- package/dist/mutations/vote-suggestion.js +36 -0
- package/dist/mutations/vote-suggestion.js.map +1 -0
- package/dist/navigate.d.ts +7 -3
- package/dist/navigate.d.ts.map +1 -1
- package/dist/navigate.js +7 -3
- package/dist/navigate.js.map +1 -1
- package/dist/ops/lineage.d.ts +11 -1
- package/dist/ops/lineage.d.ts.map +1 -1
- package/dist/ops/lineage.js +6 -2
- package/dist/ops/lineage.js.map +1 -1
- package/dist/ops/suggestions.d.ts +33 -1
- package/dist/ops/suggestions.d.ts.map +1 -1
- package/dist/ops/suggestions.js +44 -2
- package/dist/ops/suggestions.js.map +1 -1
- package/dist/ops/types.d.ts +13 -0
- package/dist/ops/types.d.ts.map +1 -1
- package/dist/ops/types.js.map +1 -1
- package/dist/place-names.d.ts +42 -0
- package/dist/place-names.d.ts.map +1 -0
- package/dist/place-names.js +43 -0
- package/dist/place-names.js.map +1 -0
- package/dist/places.d.ts +2 -0
- package/dist/places.d.ts.map +1 -1
- package/dist/places.js +2 -0
- package/dist/places.js.map +1 -1
- package/dist/scaffold/agents.d.ts +9 -1
- package/dist/scaffold/agents.d.ts.map +1 -1
- package/dist/scaffold/agents.js +33 -2
- package/dist/scaffold/agents.js.map +1 -1
- package/dist/scaffold/project.js +1 -1
- package/dist/schema/choice-words.d.ts.map +1 -1
- package/dist/schema/choice-words.js +3 -0
- package/dist/schema/choice-words.js.map +1 -1
- package/dist/starting-document.d.ts +73 -0
- package/dist/starting-document.d.ts.map +1 -0
- package/dist/starting-document.js +645 -0
- package/dist/starting-document.js.map +1 -0
- package/dist/store.d.ts +50 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +167 -27
- package/dist/store.js.map +1 -1
- package/dist/sync/connection.d.ts +20 -1
- package/dist/sync/connection.d.ts.map +1 -1
- package/dist/sync/connection.js +198 -34
- package/dist/sync/connection.js.map +1 -1
- package/dist/sync/engine.d.ts +13 -4
- package/dist/sync/engine.d.ts.map +1 -1
- package/dist/sync/engine.js +10 -6
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/google-calendar.d.ts +8 -0
- package/dist/sync/google-calendar.d.ts.map +1 -1
- package/dist/sync/google-calendar.js +16 -4
- package/dist/sync/google-calendar.js.map +1 -1
- package/dist/sync/guests.d.ts +44 -0
- package/dist/sync/guests.d.ts.map +1 -0
- package/dist/sync/guests.js +57 -0
- package/dist/sync/guests.js.map +1 -0
- package/dist/sync/notion-types.d.ts +7 -0
- package/dist/sync/notion-types.d.ts.map +1 -1
- package/dist/sync/notion-types.js +6 -1
- package/dist/sync/notion-types.js.map +1 -1
- package/dist/sync/recurrence.d.ts +41 -0
- package/dist/sync/recurrence.d.ts.map +1 -0
- package/dist/sync/recurrence.js +192 -0
- package/dist/sync/recurrence.js.map +1 -0
- package/dist/sync/types.d.ts +9 -0
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/theme/contrast.d.ts +2 -0
- package/dist/theme/contrast.d.ts.map +1 -1
- package/dist/theme/contrast.js +4 -0
- package/dist/theme/contrast.js.map +1 -1
- package/dist/theme/ground.d.ts +1 -2
- package/dist/theme/ground.d.ts.map +1 -1
- package/dist/theme/ground.js +2 -5
- package/dist/theme/ground.js.map +1 -1
- package/dist/theme/marks.d.ts +14 -2
- package/dist/theme/marks.d.ts.map +1 -1
- package/dist/theme/marks.js +20 -3
- package/dist/theme/marks.js.map +1 -1
- package/dist/theme/types.d.ts +22 -0
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/dist/tidying.d.ts +30 -4
- package/dist/tidying.d.ts.map +1 -1
- package/dist/tidying.js +110 -5
- package/dist/tidying.js.map +1 -1
- package/dist/trait-vocabulary.d.ts +17 -0
- package/dist/trait-vocabulary.d.ts.map +1 -0
- package/dist/trait-vocabulary.js +16 -0
- package/dist/trait-vocabulary.js.map +1 -0
- package/dist/traits.d.ts +79 -0
- package/dist/traits.d.ts.map +1 -0
- package/dist/traits.js +468 -0
- package/dist/traits.js.map +1 -0
- package/dist/typed.d.ts +4 -0
- package/dist/typed.d.ts.map +1 -1
- package/dist/typed.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +11 -1
package/dist/store.js
CHANGED
|
@@ -13,12 +13,13 @@ import { compileMutation } from "./mutations/define-mutation.js";
|
|
|
13
13
|
import { wouldRun } from "./mutations/would-run.js";
|
|
14
14
|
import { actSpelledAs, nearestAct } from "./mutations/act-spelling.js";
|
|
15
15
|
import { InvalidArguments } from "./mutations/invalid-arguments.js";
|
|
16
|
-
import { deriveMutations, derivedVia, removeVia } from "./mutations/derive-edits.js";
|
|
16
|
+
import { deriveMutations, derivedVia, editMutationName, editVia, removeVia } from "./mutations/derive-edits.js";
|
|
17
17
|
import { repeatingFields, repeatMakes } from "./mutations/derive-repeats.js";
|
|
18
18
|
import { namesMade, resolveMade } from "./mutations/made.js";
|
|
19
19
|
import { OperationLog } from "./ops/log.js";
|
|
20
20
|
import { lineageOf } from "./ops/lineage.js";
|
|
21
21
|
import { KEEP_LINK, keepLinkMutation, keepLinkVia } from "./mutations/keep-link.js";
|
|
22
|
+
import { VOTE_SUGGESTION, VOTING, voteSuggestionMutation } from "./mutations/vote-suggestion.js";
|
|
22
23
|
import { TYPE_TEXT, TYPING } from "./text/typing-hook.js";
|
|
23
24
|
import { declinedIn, isAutomated, tidyingRefusal } from "./ops/tidying.js";
|
|
24
25
|
import { nameOfAuthor } from "./who.js";
|
|
@@ -36,6 +37,8 @@ import { dayOf } from "./clock.js";
|
|
|
36
37
|
import { pluralOf } from "./schema/define-node.js";
|
|
37
38
|
import { linkedReaderOf } from "./schema/linked.js";
|
|
38
39
|
import { onPublish, PUBLISH, PUBLISHING } from "./publish.js";
|
|
40
|
+
import { FORMATS } from "./format-error.js";
|
|
41
|
+
import { formatStamp } from "./formats.js";
|
|
39
42
|
/** A change asked of a store of the past (Roadmap f-playback): the past is read, never changed. */
|
|
40
43
|
export class PastError extends GraphError {
|
|
41
44
|
constructor(at) {
|
|
@@ -182,11 +185,15 @@ export class Store {
|
|
|
182
185
|
keeper;
|
|
183
186
|
/** "Type text", when the store answers to its own (Roadmap f-collab-text). */
|
|
184
187
|
typer;
|
|
188
|
+
/** "Vote on a suggestion" (Roadmap f-suggestion-votes), when the app declared no act by that name. */
|
|
189
|
+
voter;
|
|
185
190
|
invariants;
|
|
186
191
|
invariantOptions;
|
|
187
192
|
policy;
|
|
188
193
|
/** When edits reach readers (`StoreOptions.publishing`). */
|
|
189
194
|
publishing;
|
|
195
|
+
/** What the declaration says of suggestions as a whole (`StoreOptions.tidying`). */
|
|
196
|
+
tidying;
|
|
190
197
|
/**
|
|
191
198
|
* The intelligence the app declared, so a surface can read the DOORS.
|
|
192
199
|
*
|
|
@@ -249,6 +256,7 @@ export class Store {
|
|
|
249
256
|
this.ticked = { day: isoToday(this.timeZone), on: isoToday(this.timeZone) };
|
|
250
257
|
this.policy = options.policy;
|
|
251
258
|
this.publishing = options.publishing;
|
|
259
|
+
this.tidying = options.tidying;
|
|
252
260
|
this.by = options.principal ?? HUMAN;
|
|
253
261
|
this.intelligence = options.intelligence ?? [];
|
|
254
262
|
for (const provider of options.intelligence ?? []) {
|
|
@@ -286,6 +294,11 @@ export class Store {
|
|
|
286
294
|
this.keeper = keepLinkMutation(options.schema);
|
|
287
295
|
this.mutations.set(KEEP_LINK, this.keeper);
|
|
288
296
|
}
|
|
297
|
+
// "Vote on a suggestion" (Roadmap f-suggestion-votes): answered by every store, offered by no list. An app's own `vote-suggestion` keeps its name.
|
|
298
|
+
if (!options.past && !this.mutations.has(VOTE_SUGGESTION)) {
|
|
299
|
+
this.voter = voteSuggestionMutation();
|
|
300
|
+
this.mutations.set(VOTE_SUGGESTION, this.voter);
|
|
301
|
+
}
|
|
289
302
|
const horizon = given("horizon", options.horizon);
|
|
290
303
|
const validate = { validate: this.validate };
|
|
291
304
|
if (options.log) {
|
|
@@ -494,7 +507,7 @@ export class Store {
|
|
|
494
507
|
return mutation.derived !== undefined && this.modules.disabledKinds.has(mutation.derived.kind);
|
|
495
508
|
}
|
|
496
509
|
allMutations() {
|
|
497
|
-
return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && !this.mutationDisabled(mutation));
|
|
510
|
+
return [...this.mutations.values(), ...(this.publisher ? [this.publisher] : [])].filter((mutation) => mutation !== this.keeper && mutation !== this.typer && mutation !== this.voter && !this.mutationDisabled(mutation));
|
|
498
511
|
}
|
|
499
512
|
/** The declared acts a derived act resolves its permission through; keeping a link, those that make or break its relation. */
|
|
500
513
|
viaOf(mutation, call, principal) {
|
|
@@ -565,6 +578,9 @@ export class Store {
|
|
|
565
578
|
// Publishing is the declaration's `publishers`, not a grant's.
|
|
566
579
|
if (mutation && mutation === this.publisher)
|
|
567
580
|
return PUBLISHING.verdict(this, principal);
|
|
581
|
+
// A vote on a suggestion is anybody's who may see it (Roadmap f-suggestion-votes): whether they do is judged where the op is made.
|
|
582
|
+
if (mutation && mutation === this.voter)
|
|
583
|
+
return { ok: true };
|
|
568
584
|
/*
|
|
569
585
|
* KEEPING A LINK ONLY THE DERIVED ACTS MAKE is judged as they are: on
|
|
570
586
|
* the record it leaves, so riding everybody's grant it reaches only the
|
|
@@ -598,6 +614,17 @@ export class Store {
|
|
|
598
614
|
mutation !== undefined && (mutation.derived !== undefined || mutation === this.typer || keeping) ? () => this.madeBy(mutation, call, id) : undefined,
|
|
599
615
|
// Taking a record out, or folding it away, rides the making of the kind only over what this principal made (W-201).
|
|
600
616
|
mutation?.derived !== undefined && UNMAKING.has(mutation.derived.act));
|
|
617
|
+
/*
|
|
618
|
+
* A VALUE CARRIED ACROSS A LINK (Roadmap f-acts-update-linked) is each
|
|
619
|
+
* far record's own edit: the act is refused, naming the record, where
|
|
620
|
+
* this principal may not change one of the records its subject is
|
|
621
|
+
* linked to — judged here, before `apply`, as every grant is.
|
|
622
|
+
*/
|
|
623
|
+
if (verdict.ok && mutation?.writesLinked && typeof id === "string") {
|
|
624
|
+
const refused = this.refusesLinked(mutation, id, principal);
|
|
625
|
+
if (refused)
|
|
626
|
+
return refused;
|
|
627
|
+
}
|
|
601
628
|
if (!verdict.ok || !mutation?.derived || !this.repeatMakes(mutation, call, id, principal))
|
|
602
629
|
return verdict;
|
|
603
630
|
/*
|
|
@@ -608,6 +635,44 @@ export class Store {
|
|
|
608
635
|
*/
|
|
609
636
|
return permits(this.policy, principal, call.name, kind, removeVia([...this.mutations.values()], mutation.derived.kind), typeof id === "string" ? id : undefined, this.words);
|
|
610
637
|
}
|
|
638
|
+
/**
|
|
639
|
+
* The first record linked to the subject that this principal may not
|
|
640
|
+
* change, as a refusal of the act naming it (Roadmap f-acts-update-linked);
|
|
641
|
+
* undefined when every record the act would carry a value to is theirs to
|
|
642
|
+
* edit. Each is judged as its kind's own edit is — the grants, the acts the
|
|
643
|
+
* edit rides, what the principal made — over the graph as they see it.
|
|
644
|
+
*/
|
|
645
|
+
refusesLinked(mutation, subjectId, principal) {
|
|
646
|
+
const served = isSystem(principal) ? this.graph : seenBy(this, principal).graph;
|
|
647
|
+
const subject = served.getNode(subjectId);
|
|
648
|
+
if (!subject)
|
|
649
|
+
return undefined;
|
|
650
|
+
const all = [...this.mutations.values()];
|
|
651
|
+
for (const relation of Object.keys(mutation.writesLinked ?? {})) {
|
|
652
|
+
const declaredHere = this.schema.tryDefinition(subject.kind)?.edges?.[relation] !== undefined;
|
|
653
|
+
const far = declaredHere ? served.out(subject.id, relation) : served.in(subject.id, relation);
|
|
654
|
+
for (const node of far) {
|
|
655
|
+
const edit = editMutationName(node.kind);
|
|
656
|
+
const own = this.mutations.get(edit);
|
|
657
|
+
const verdict = own ? this.permits({ name: edit, args: { [own.subject?.arg ?? "id"]: node.id } }, principal) : permits(this.policy, principal, edit, node.kind, editVia(this.schema, all, node.kind), node.id, this.words);
|
|
658
|
+
if (verdict.ok)
|
|
659
|
+
continue;
|
|
660
|
+
const title = this.words.act?.(mutation.name) ?? mutation.name;
|
|
661
|
+
const label = (this.labels ??= new LabelIndex(this.graph)).labelOf(node.id) ?? node.id;
|
|
662
|
+
const noun = this.words.noun?.(node.kind) ?? node.kind;
|
|
663
|
+
return {
|
|
664
|
+
ok: false,
|
|
665
|
+
refusal: {
|
|
666
|
+
mutation: mutation.name,
|
|
667
|
+
kind: subject.kind,
|
|
668
|
+
wouldNeed: verdict.refusal.wouldNeed,
|
|
669
|
+
message: `Not permitted: “${title}” would change “${label}”, ${/^[aeiou]/i.test(noun) ? "an" : "a"} ${noun} that is not yours to change. ${verdict.refusal.message}`,
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
611
676
|
/** Whether a call of a repeat act makes a record (`repeatMakes`), read off the series as this principal is served it. */
|
|
612
677
|
repeatMakes(mutation, call, id, principal) {
|
|
613
678
|
const act = mutation.derived.act;
|
|
@@ -1116,6 +1181,23 @@ export class Store {
|
|
|
1116
1181
|
Object.assign(twin, { typer, mutations: typer ? new Map(this.mutations).set(TYPE_TEXT, typer) : new Map(this.mutations) });
|
|
1117
1182
|
return twin;
|
|
1118
1183
|
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Whether these calls, as this author with this lineage, would be kept
|
|
1186
|
+
* rather than refused — rehearsed, told to nobody. What a vote asks
|
|
1187
|
+
* before it has the app's own actor make a suggestion that reached
|
|
1188
|
+
* `autoAcceptAt` (Roadmap f-suggestion-votes): a record a person said to
|
|
1189
|
+
* leave alone, or a change a person declined meanwhile, leaves the
|
|
1190
|
+
* suggestion waiting for someone who may, and the vote stands.
|
|
1191
|
+
*/
|
|
1192
|
+
wouldKeep(calls, author, lineage) {
|
|
1193
|
+
try {
|
|
1194
|
+
this.rehearsal().applying(calls, { author, lineage });
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
catch {
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1119
1201
|
previewPrimitives(primitives, meta) {
|
|
1120
1202
|
const before = this.violations(meta.context);
|
|
1121
1203
|
const trial = Graph.from(this.schema, this.graph.snapshot());
|
|
@@ -1178,20 +1260,29 @@ export class Store {
|
|
|
1178
1260
|
* seat holds say what it may do at all; `may` narrows what THIS provider
|
|
1179
1261
|
* was brought in for, and the narrower of the two wins.
|
|
1180
1262
|
*/
|
|
1181
|
-
const permitted = (call) => {
|
|
1182
|
-
const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call,
|
|
1263
|
+
const permitted = (call, as = author) => {
|
|
1264
|
+
const verdict = this.policy || (this.publisher && call.name === PUBLISH) ? this.permits(call, as) : undefined;
|
|
1183
1265
|
if (verdict && !verdict.ok)
|
|
1184
|
-
this.refuse(verdict.refusal,
|
|
1266
|
+
this.refuse(verdict.refusal, as.id);
|
|
1185
1267
|
};
|
|
1186
|
-
const declared = (call) => {
|
|
1187
|
-
const refusal = this.refusesAgent(call,
|
|
1268
|
+
const declared = (call, as = author) => {
|
|
1269
|
+
const refusal = this.refusesAgent(call, as) ?? this.namesTurnedOff(call, as);
|
|
1188
1270
|
if (refusal)
|
|
1189
|
-
this.refuse(refusal,
|
|
1271
|
+
this.refuse(refusal, as.id);
|
|
1190
1272
|
};
|
|
1191
1273
|
/* A call that names a record an earlier call makes (`{ $made: n }`, FR-193) is asked once that record is there to name. */
|
|
1192
1274
|
const naming = calls.map(namesMade);
|
|
1193
1275
|
calls.forEach((call, at) => naming[at] || permitted(call));
|
|
1194
1276
|
calls.forEach((call, at) => naming[at] || declared(call));
|
|
1277
|
+
/*
|
|
1278
|
+
* THE GESTURE'S CALLS, AND WHAT ONE OF THEM HAS THE APP'S OWN ACTOR MAKE
|
|
1279
|
+
* (Roadmap f-suggestion-votes): a vote that brings a suggestion to the
|
|
1280
|
+
* declaration's `autoAcceptAt` queues the suggestion's calls behind it,
|
|
1281
|
+
* as the actor, in this same gesture — one batch, one undo, one answer
|
|
1282
|
+
* to whoever sent it — and the people who voted decided it, so the
|
|
1283
|
+
* declaration's `tidying` does not stand in the actor's way (`decided`).
|
|
1284
|
+
*/
|
|
1285
|
+
const queue = calls.map((sent) => ({ sent, as: author, lineage, decided: false }));
|
|
1195
1286
|
/*
|
|
1196
1287
|
* THE APP'S OWN ACTOR IS HELD TO WHAT PEOPLE SAID: a change a person
|
|
1197
1288
|
* declined is not made again, a record a person said to leave alone is
|
|
@@ -1216,38 +1307,55 @@ export class Store {
|
|
|
1216
1307
|
const renamed = new Map();
|
|
1217
1308
|
const rename = (value) => (typeof value === "string" ? (renamed.get(value) ?? value) : Array.isArray(value) ? value.map(rename) : value);
|
|
1218
1309
|
try {
|
|
1219
|
-
for (
|
|
1310
|
+
for (let at = 0; at < queue.length; at++) {
|
|
1311
|
+
const { sent, as, lineage: lined, decided } = queue[at];
|
|
1312
|
+
const named = at < naming.length ? naming[at] : namesMade(sent);
|
|
1220
1313
|
const { mintedHint: hint, ...plain } = sent;
|
|
1221
1314
|
const asked = renamed.size ? { ...plain, args: Object.fromEntries(Object.entries(plain.args).map(([key, value]) => [key, rename(value)])) } : plain;
|
|
1222
|
-
const call =
|
|
1223
|
-
if (
|
|
1224
|
-
(permitted(call), declared(call));
|
|
1315
|
+
const call = named ? resolveMade(asked, madeIds, at) : asked;
|
|
1316
|
+
if (named || decided)
|
|
1317
|
+
(permitted(call, as), declared(call, as));
|
|
1225
1318
|
const definition = this.mutation(call.name);
|
|
1226
1319
|
if (definition === this.publisher) {
|
|
1227
|
-
const op = PUBLISHING.op(this, { id: this.nextId(), at: this.now() }, call, batch,
|
|
1320
|
+
const op = PUBLISHING.op(this, { id: this.nextId(), at: this.now() }, call, batch, as, options);
|
|
1228
1321
|
ops.push(op);
|
|
1229
1322
|
intents.push(op.intent);
|
|
1230
1323
|
continue;
|
|
1231
1324
|
}
|
|
1232
|
-
|
|
1325
|
+
if (definition === this.voter) {
|
|
1326
|
+
// A vote on a suggestion (Roadmap f-suggestion-votes): its op, judged as the voter sees the suggestion, and nothing of the graph.
|
|
1327
|
+
if (!VOTING.op)
|
|
1328
|
+
throw new GraphError("Load @graview/core/tidying to vote on a suggestion.");
|
|
1329
|
+
const op = VOTING.op(this, { id: this.nextId(), at: this.now() }, call, batch, as, options);
|
|
1330
|
+
this.log.append(op);
|
|
1331
|
+
ops.push(op);
|
|
1332
|
+
intents.push(op.intent);
|
|
1333
|
+
// At the declaration's `autoAcceptAt`, the suggestion is made here, in the same gesture, by the app's own actor — when the actor would be let.
|
|
1334
|
+
const accept = VOTING.accepting?.(this, op);
|
|
1335
|
+
if (accept && this.wouldKeep(accept.calls, accept.author, accept.lineage))
|
|
1336
|
+
for (const made of accept.calls)
|
|
1337
|
+
queue.push({ sent: made, as: accept.author, lineage: lineageOf(accept.lineage), decided: true });
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
const missing = this.missingFor(call, as);
|
|
1233
1341
|
if (missing)
|
|
1234
1342
|
throw missing;
|
|
1235
1343
|
let compiled;
|
|
1236
1344
|
try {
|
|
1237
|
-
compiled = compileMutation(this.graph, definition, call.args, { ...this.sighted(
|
|
1345
|
+
compiled = compileMutation(this.graph, definition, call.args, { ...this.sighted(as), hint });
|
|
1238
1346
|
}
|
|
1239
1347
|
catch (error) {
|
|
1240
1348
|
// A press the act's own input refused: the watch tells a form filled wrongly from an act offered whole.
|
|
1241
1349
|
if (error instanceof InvalidArguments && !this.rehearsing)
|
|
1242
|
-
tellTheWatchOfAnInputRefusal(error,
|
|
1350
|
+
tellTheWatchOfAnInputRefusal(error, as.id);
|
|
1243
1351
|
throw error;
|
|
1244
1352
|
}
|
|
1245
1353
|
if (hint !== undefined && compiled.firstMinted !== undefined && compiled.firstMinted !== hint)
|
|
1246
1354
|
renamed.set(hint, compiled.firstMinted);
|
|
1247
|
-
if (
|
|
1248
|
-
const refused = tidyingRefusal(this.schema, this.graph, compiled.primitives, this.authorName(
|
|
1355
|
+
if (isAutomated(as)) {
|
|
1356
|
+
const refused = tidyingRefusal(this.schema, this.graph, compiled.primitives, this.authorName(as), { declaration: !this.suggesting && !decided });
|
|
1249
1357
|
if (refused)
|
|
1250
|
-
this.refuse({ mutation: call.name, message: refused, wouldNeed: [] },
|
|
1358
|
+
this.refuse({ mutation: call.name, message: refused, wouldNeed: [] }, as.id);
|
|
1251
1359
|
}
|
|
1252
1360
|
/*
|
|
1253
1361
|
* AN ACT THAT DID NOTHING DOES NOT GO IN THE HISTORY.
|
|
@@ -1268,7 +1376,7 @@ export class Store {
|
|
|
1268
1376
|
id: this.nextId(),
|
|
1269
1377
|
seq: this.log.length,
|
|
1270
1378
|
batch,
|
|
1271
|
-
author,
|
|
1379
|
+
author: as,
|
|
1272
1380
|
/*
|
|
1273
1381
|
* THE ACT'S OWN SENTENCE, kept; what the gesture was for goes
|
|
1274
1382
|
* beside it (FR-18). An act with no `describe` has no sentence of
|
|
@@ -1292,7 +1400,7 @@ export class Store {
|
|
|
1292
1400
|
...given("mergedInto", compiled.mergedInto),
|
|
1293
1401
|
at: this.now(),
|
|
1294
1402
|
...given("via", options.via),
|
|
1295
|
-
...given("lineage",
|
|
1403
|
+
...given("lineage", lined),
|
|
1296
1404
|
};
|
|
1297
1405
|
this.graph.applyPrimitives(op.primitives);
|
|
1298
1406
|
this.log.append(op);
|
|
@@ -1679,6 +1787,13 @@ export class Store {
|
|
|
1679
1787
|
*/
|
|
1680
1788
|
const judged = this.rootOf(target);
|
|
1681
1789
|
const by = actingAs(judged.author).id;
|
|
1790
|
+
// A vote is its voter's to take back (Roadmap f-suggestion-votes), the system's, or a seat's the policy grants every act.
|
|
1791
|
+
if (judged.vote !== undefined) {
|
|
1792
|
+
if (this.policy && !isSystem(author) && by !== actingAs(author).id && !this.grantsEveryAct(author)) {
|
|
1793
|
+
return { mutation: "undo", wouldNeed: [], message: `Not permitted to undo "${target.intent}": only whoever voted takes their vote back.` };
|
|
1794
|
+
}
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1682
1797
|
if (!judged.mutation) {
|
|
1683
1798
|
// A change that changed nothing (a suggestion, an announcement) is anybody's who sees it to take back: declining a suggestion is one.
|
|
1684
1799
|
if (judged.primitives.length === 0)
|
|
@@ -2053,19 +2168,44 @@ export class Store {
|
|
|
2053
2168
|
const pinned = [...(options.pins ?? []), ...readers].filter((pin) => Number.isInteger(pin));
|
|
2054
2169
|
if (pinned.length > 0)
|
|
2055
2170
|
seq = Math.min(seq, ...pinned);
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2171
|
+
const format = options.format ?? FORMATS.snapshot;
|
|
2172
|
+
if (!Number.isInteger(format) || format < 1 || format > FORMATS.snapshot) {
|
|
2173
|
+
throw new RangeError(`Cannot write a checkpoint in snapshot format ${String(format)}: this build writes formats 1 to ${FORMATS.snapshot}.`);
|
|
2174
|
+
}
|
|
2175
|
+
// Never past the head or behind the horizon, and never inside a gesture: back to where the batch it would split began.
|
|
2176
|
+
const settled = (asked) => {
|
|
2177
|
+
let at = Math.min(Math.max(asked, this.log.horizon), this.log.length);
|
|
2178
|
+
while (at > this.log.horizon && at < this.log.length && ops[at - this.log.horizon].batch === ops[at - 1 - this.log.horizon].batch)
|
|
2179
|
+
at--;
|
|
2180
|
+
return at;
|
|
2181
|
+
};
|
|
2182
|
+
seq = settled(seq);
|
|
2060
2183
|
if (seq <= this.log.horizon)
|
|
2061
2184
|
return undefined;
|
|
2062
2185
|
const verified = this.verify();
|
|
2063
2186
|
if (!verified.ok)
|
|
2064
2187
|
throw new GraphError(`Cannot compact a store that does not verify: ${verified.reason}`);
|
|
2188
|
+
let checkpoint = this.checkpointWithPublished(seq);
|
|
2065
2189
|
/*
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
2190
|
+
* READABLE AS THE FORMAT ASKED FOR (FR-31): a checkpoint keeping the app as readers are served at a seq behind it
|
|
2191
|
+
* (`published`) is snapshot format 2, which a build before it would misread (`formatStamp`). Asked for format 1,
|
|
2192
|
+
* the horizon is held at the earliest such seq instead — and again where that lands past another — until the stamp
|
|
2193
|
+
* says 1; the checkpoint's seq says where it stopped, and none is made when that is the horizon already.
|
|
2068
2194
|
*/
|
|
2195
|
+
while (formatStamp(checkpoint).formats.snapshot > format) {
|
|
2196
|
+
seq = settled(Math.min(...checkpoint.published.map((kept) => kept.seq)));
|
|
2197
|
+
if (seq <= this.log.horizon)
|
|
2198
|
+
return undefined;
|
|
2199
|
+
checkpoint = this.checkpointWithPublished(seq);
|
|
2200
|
+
}
|
|
2201
|
+
return checkpoint;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* The checkpoint at `seq`, and the app as readers were served it at each published seq behind it they may be served
|
|
2205
|
+
* again: folded here where the log still holds it, carried from the checkpoint before where it does not, so a horizon
|
|
2206
|
+
* past old publishes loses none.
|
|
2207
|
+
*/
|
|
2208
|
+
checkpointWithPublished(seq) {
|
|
2069
2209
|
const carried = this.log.horizonEpoch()?.published ?? [];
|
|
2070
2210
|
const published = (PUBLISHING.behind?.(this, seq) ?? []).flatMap((at) => (at >= this.log.horizon ? [this.epochAt(at)] : carried.filter((one) => one.seq === at)));
|
|
2071
2211
|
return { ...this.epochAt(seq), ...(published.length > 0 && { published }) };
|