@graview/core 0.1.27 → 0.1.29
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 +17 -0
- package/dist/amount-input.d.ts +21 -0
- package/dist/amount-input.d.ts.map +1 -0
- package/dist/amount-input.js +96 -0
- package/dist/amount-input.js.map +1 -0
- package/dist/answer-seen.d.ts +38 -0
- package/dist/answer-seen.d.ts.map +1 -0
- package/dist/answer-seen.js +122 -0
- package/dist/answer-seen.js.map +1 -0
- package/dist/app-places.d.ts +56 -0
- package/dist/app-places.d.ts.map +1 -0
- package/dist/app-places.js +153 -0
- package/dist/app-places.js.map +1 -0
- package/dist/arrange.d.ts +2 -2
- package/dist/arrange.d.ts.map +1 -1
- package/dist/arrange.js +2 -2
- package/dist/arrange.js.map +1 -1
- package/dist/arrangement-words.d.ts +3 -0
- package/dist/arrangement-words.d.ts.map +1 -0
- package/dist/arrangement-words.js +50 -0
- package/dist/arrangement-words.js.map +1 -0
- package/dist/arrangement.d.ts +25 -3
- package/dist/arrangement.d.ts.map +1 -1
- package/dist/arrangement.js +36 -32
- package/dist/arrangement.js.map +1 -1
- package/dist/arranging.d.ts.map +1 -1
- package/dist/arranging.js +19 -8
- package/dist/arranging.js.map +1 -1
- package/dist/bind.d.ts +3 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js.map +1 -1
- package/dist/capabilities.js +1 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/capital.d.ts +2 -0
- package/dist/capital.d.ts.map +1 -1
- package/dist/capital.js +2 -0
- package/dist/capital.js.map +1 -1
- package/dist/check.d.ts +3 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +3 -0
- package/dist/check.js.map +1 -1
- package/dist/city.d.ts +58 -50
- package/dist/city.d.ts.map +1 -1
- package/dist/city.js +160 -147
- package/dist/city.js.map +1 -1
- package/dist/cli/check/brand.js +1 -1
- package/dist/cli/check/brand.js.map +1 -1
- package/dist/cli/check/clock-rules.d.ts +4 -0
- package/dist/cli/check/clock-rules.d.ts.map +1 -0
- package/dist/cli/check/clock-rules.js +26 -0
- package/dist/cli/check/clock-rules.js.map +1 -0
- package/dist/cli/check/field-sight.d.ts +11 -0
- package/dist/cli/check/field-sight.d.ts.map +1 -0
- package/dist/cli/check/field-sight.js +113 -0
- package/dist/cli/check/field-sight.js.map +1 -0
- package/dist/cli/check/index.d.ts +2 -0
- package/dist/cli/check/index.d.ts.map +1 -1
- package/dist/cli/check/index.js +2 -0
- package/dist/cli/check/index.js.map +1 -1
- package/dist/cli/check/lenses.d.ts +1 -1
- package/dist/cli/check/lenses.d.ts.map +1 -1
- package/dist/cli/check/lenses.js +43 -2
- package/dist/cli/check/lenses.js.map +1 -1
- package/dist/cli/check/relations.d.ts.map +1 -1
- package/dist/cli/check/relations.js +2 -1
- package/dist/cli/check/relations.js.map +1 -1
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +7 -4
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/describe.d.ts.map +1 -1
- package/dist/cli/describe.js +44 -3
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/docs.d.ts.map +1 -1
- package/dist/cli/docs.js +4 -3
- package/dist/cli/docs.js.map +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js +10 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/clock-readers.d.ts +18 -0
- package/dist/clock-readers.d.ts.map +1 -0
- package/dist/clock-readers.js +70 -0
- package/dist/clock-readers.js.map +1 -0
- package/dist/clock.d.ts +30 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +41 -0
- package/dist/clock.js.map +1 -0
- package/dist/columns.d.ts.map +1 -1
- package/dist/columns.js +2 -1
- package/dist/columns.js.map +1 -1
- package/dist/conformance/fixtures.d.ts.map +1 -1
- package/dist/conformance/fixtures.js +326 -43
- package/dist/conformance/fixtures.js.map +1 -1
- package/dist/days.d.ts +53 -4
- package/dist/days.d.ts.map +1 -1
- package/dist/days.js +136 -16
- package/dist/days.js.map +1 -1
- package/dist/describe.d.ts +1 -0
- package/dist/describe.d.ts.map +1 -1
- package/dist/describe.js +1 -0
- package/dist/describe.js.map +1 -1
- package/dist/document/act-findings.d.ts.map +1 -1
- package/dist/document/act-findings.js +13 -3
- package/dist/document/act-findings.js.map +1 -1
- package/dist/document/amount.d.ts +4 -0
- package/dist/document/amount.d.ts.map +1 -0
- package/dist/document/amount.js +22 -0
- package/dist/document/amount.js.map +1 -0
- package/dist/document/block-context.d.ts.map +1 -1
- package/dist/document/block-context.js +1 -0
- package/dist/document/block-context.js.map +1 -1
- package/dist/document/blocks.d.ts +13 -3
- package/dist/document/blocks.d.ts.map +1 -1
- package/dist/document/blocks.js +56 -12
- package/dist/document/blocks.js.map +1 -1
- package/dist/document/compile-checked.d.ts.map +1 -1
- package/dist/document/compile-checked.js +3 -2
- package/dist/document/compile-checked.js.map +1 -1
- package/dist/document/compile.d.ts.map +1 -1
- package/dist/document/compile.js +30 -7
- package/dist/document/compile.js.map +1 -1
- package/dist/document/compiled.d.ts +4 -2
- package/dist/document/compiled.d.ts.map +1 -1
- package/dist/document/compiled.js +52 -21
- package/dist/document/compiled.js.map +1 -1
- package/dist/document/describe-coverage.d.ts.map +1 -1
- package/dist/document/describe-coverage.js +2 -1
- package/dist/document/describe-coverage.js.map +1 -1
- package/dist/document/describe-place.d.ts +6 -2
- package/dist/document/describe-place.d.ts.map +1 -1
- package/dist/document/describe-place.js +14 -8
- package/dist/document/describe-place.js.map +1 -1
- package/dist/document/diff.d.ts +2 -0
- package/dist/document/diff.d.ts.map +1 -1
- package/dist/document/diff.js +78 -7
- package/dist/document/diff.js.map +1 -1
- package/dist/document/durations.d.ts +8 -0
- package/dist/document/durations.d.ts.map +1 -0
- package/dist/document/durations.js +82 -0
- package/dist/document/durations.js.map +1 -0
- package/dist/document/edit.d.ts +1 -1
- package/dist/document/edit.d.ts.map +1 -1
- package/dist/document/edit.js +434 -34
- package/dist/document/edit.js.map +1 -1
- package/dist/document/expr/evaluate.d.ts +1 -1
- package/dist/document/expr/evaluate.d.ts.map +1 -1
- package/dist/document/expr/evaluate.js +17 -4
- package/dist/document/expr/evaluate.js.map +1 -1
- package/dist/document/field-spec.d.ts.map +1 -1
- package/dist/document/field-spec.js +15 -1
- package/dist/document/field-spec.js.map +1 -1
- package/dist/document/format-findings.d.ts +4 -0
- package/dist/document/format-findings.d.ts.map +1 -0
- package/dist/document/format-findings.js +39 -0
- package/dist/document/format-findings.js.map +1 -0
- package/dist/document/format-rules.d.ts +8 -0
- package/dist/document/format-rules.d.ts.map +1 -0
- package/dist/document/format-rules.js +19 -0
- package/dist/document/format-rules.js.map +1 -0
- package/dist/document/format-words.d.ts +9 -0
- package/dist/document/format-words.d.ts.map +1 -0
- package/dist/document/format-words.js +29 -0
- package/dist/document/format-words.js.map +1 -0
- package/dist/document/graview-template.d.ts +2 -2
- package/dist/document/migrate.d.ts +1 -0
- package/dist/document/migrate.d.ts.map +1 -1
- package/dist/document/migrate.js +45 -1
- package/dist/document/migrate.js.map +1 -1
- package/dist/document/money.d.ts +14 -0
- package/dist/document/money.d.ts.map +1 -0
- package/dist/document/money.js +14 -0
- package/dist/document/money.js.map +1 -0
- package/dist/document/policy-words.d.ts +33 -0
- package/dist/document/policy-words.d.ts.map +1 -0
- package/dist/document/policy-words.js +69 -0
- package/dist/document/policy-words.js.map +1 -0
- package/dist/document/rule-line.d.ts.map +1 -1
- package/dist/document/rule-line.js +16 -12
- package/dist/document/rule-line.js.map +1 -1
- package/dist/document/rules.d.ts.map +1 -1
- package/dist/document/rules.js +6 -5
- package/dist/document/rules.js.map +1 -1
- package/dist/document/schema.d.ts +62 -19
- package/dist/document/schema.d.ts.map +1 -1
- package/dist/document/schema.js +30 -4
- package/dist/document/schema.js.map +1 -1
- package/dist/document/template-parse.d.ts.map +1 -1
- package/dist/document/template-parse.js +9 -2
- package/dist/document/template-parse.js.map +1 -1
- package/dist/document/template.d.ts +3 -13
- package/dist/document/template.d.ts.map +1 -1
- package/dist/document/template.js +27 -28
- package/dist/document/template.js.map +1 -1
- package/dist/document/thumbnail.d.ts.map +1 -1
- package/dist/document/thumbnail.js +4 -3
- package/dist/document/thumbnail.js.map +1 -1
- package/dist/document/to-document.d.ts +0 -1
- package/dist/document/to-document.d.ts.map +1 -1
- package/dist/document/to-document.js +19 -2
- package/dist/document/to-document.js.map +1 -1
- package/dist/document/view-specs.d.ts.map +1 -1
- package/dist/document/view-specs.js +3 -0
- package/dist/document/view-specs.js.map +1 -1
- package/dist/document/views.d.ts +2 -0
- package/dist/document/views.d.ts.map +1 -1
- package/dist/document/views.js +18 -6
- package/dist/document/views.js.map +1 -1
- package/dist/document.schema.json +63 -6
- package/dist/excerpt-of.d.ts +14 -0
- package/dist/excerpt-of.d.ts.map +1 -0
- package/dist/excerpt-of.js +120 -0
- package/dist/excerpt-of.js.map +1 -0
- package/dist/excerpt.d.ts +9 -0
- package/dist/excerpt.d.ts.map +1 -0
- package/dist/excerpt.js +43 -0
- package/dist/excerpt.js.map +1 -0
- package/dist/field-sight.d.ts +33 -0
- package/dist/field-sight.d.ts.map +1 -0
- package/dist/field-sight.js +313 -0
- package/dist/field-sight.js.map +1 -0
- package/dist/forms.d.ts +1 -0
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +1 -0
- package/dist/forms.js.map +1 -1
- package/dist/graph/archived.d.ts +25 -0
- package/dist/graph/archived.d.ts.map +1 -0
- package/dist/graph/archived.js +16 -0
- package/dist/graph/archived.js.map +1 -0
- package/dist/graph/diff.d.ts.map +1 -1
- package/dist/graph/diff.js +2 -1
- package/dist/graph/diff.js.map +1 -1
- package/dist/graph/graph.d.ts +37 -13
- package/dist/graph/graph.d.ts.map +1 -1
- package/dist/graph/graph.js +86 -40
- package/dist/graph/graph.js.map +1 -1
- package/dist/graph/tracked.d.ts +8 -8
- package/dist/graph/tracked.d.ts.map +1 -1
- package/dist/graph/tracked.js +15 -20
- package/dist/graph/tracked.js.map +1 -1
- package/dist/graph/types.d.ts +16 -8
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/types.js.map +1 -1
- package/dist/import/coerce.d.ts +45 -0
- package/dist/import/coerce.d.ts.map +1 -0
- package/dist/import/coerce.js +252 -0
- package/dist/import/coerce.js.map +1 -0
- package/dist/import/csv.d.ts +16 -0
- package/dist/import/csv.d.ts.map +1 -0
- package/dist/import/csv.js +77 -0
- package/dist/import/csv.js.map +1 -0
- package/dist/import/index.d.ts +17 -0
- package/dist/import/index.d.ts.map +1 -0
- package/dist/import/index.js +13 -0
- package/dist/import/index.js.map +1 -0
- package/dist/import/mapping.d.ts +99 -0
- package/dist/import/mapping.d.ts.map +1 -0
- package/dist/import/mapping.js +182 -0
- package/dist/import/mapping.js.map +1 -0
- package/dist/import/rows.d.ts +116 -0
- package/dist/import/rows.d.ts.map +1 -0
- package/dist/import/rows.js +584 -0
- package/dist/import/rows.js.map +1 -0
- package/dist/index.d.ts +38 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -16
- package/dist/index.js.map +1 -1
- package/dist/invariants/engine.d.ts +5 -1
- package/dist/invariants/engine.d.ts.map +1 -1
- package/dist/invariants/engine.js +4 -3
- package/dist/invariants/engine.js.map +1 -1
- package/dist/invariants/types.d.ts +18 -3
- package/dist/invariants/types.d.ts.map +1 -1
- package/dist/json-schema.d.ts +1 -0
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +2 -0
- package/dist/json-schema.js.map +1 -1
- package/dist/labels.d.ts.map +1 -1
- package/dist/labels.js +2 -1
- package/dist/labels.js.map +1 -1
- package/dist/lines.d.ts +1 -0
- package/dist/lines.d.ts.map +1 -1
- package/dist/lines.js +5 -3
- package/dist/lines.js.map +1 -1
- package/dist/markdown.d.ts +102 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +460 -0
- package/dist/markdown.js.map +1 -0
- package/dist/mutations/arg-shape.d.ts +95 -0
- package/dist/mutations/arg-shape.d.ts.map +1 -0
- package/dist/mutations/arg-shape.js +101 -0
- package/dist/mutations/arg-shape.js.map +1 -0
- package/dist/mutations/define-mutation.d.ts +48 -2
- package/dist/mutations/define-mutation.d.ts.map +1 -1
- package/dist/mutations/define-mutation.js +103 -36
- package/dist/mutations/define-mutation.js.map +1 -1
- package/dist/mutations/derive-edits.d.ts +16 -1
- package/dist/mutations/derive-edits.d.ts.map +1 -1
- package/dist/mutations/derive-edits.js +67 -28
- package/dist/mutations/derive-edits.js.map +1 -1
- package/dist/mutations/derive-repeats.d.ts +8 -0
- package/dist/mutations/derive-repeats.d.ts.map +1 -0
- package/dist/mutations/derive-repeats.js +118 -0
- package/dist/mutations/derive-repeats.js.map +1 -0
- package/dist/mutations/derived-names.d.ts +20 -0
- package/dist/mutations/derived-names.d.ts.map +1 -0
- package/dist/mutations/derived-names.js +19 -0
- package/dist/mutations/derived-names.js.map +1 -0
- package/dist/mutations/form.d.ts +21 -1
- package/dist/mutations/form.d.ts.map +1 -1
- package/dist/mutations/form.js +19 -2
- package/dist/mutations/form.js.map +1 -1
- package/dist/mutations/invalid-arguments.d.ts +14 -0
- package/dist/mutations/invalid-arguments.d.ts.map +1 -0
- package/dist/mutations/invalid-arguments.js +18 -0
- package/dist/mutations/invalid-arguments.js.map +1 -0
- package/dist/mutations/keep-link.d.ts +27 -0
- package/dist/mutations/keep-link.d.ts.map +1 -0
- package/dist/mutations/keep-link.js +56 -0
- package/dist/mutations/keep-link.js.map +1 -0
- package/dist/mutations/kept-arguments.d.ts +26 -0
- package/dist/mutations/kept-arguments.d.ts.map +1 -0
- package/dist/mutations/kept-arguments.js +77 -0
- package/dist/mutations/kept-arguments.js.map +1 -0
- package/dist/mutations/node-ref.d.ts +0 -80
- package/dist/mutations/node-ref.d.ts.map +1 -1
- package/dist/mutations/node-ref.js +0 -93
- package/dist/mutations/node-ref.js.map +1 -1
- package/dist/mutations/types.d.ts +17 -1
- package/dist/mutations/types.d.ts.map +1 -1
- package/dist/mutations/words.d.ts +0 -13
- package/dist/mutations/words.d.ts.map +1 -1
- package/dist/mutations/words.js +1 -12
- package/dist/mutations/words.js.map +1 -1
- package/dist/ops/kept-fields.d.ts +7 -0
- package/dist/ops/kept-fields.d.ts.map +1 -0
- package/dist/ops/kept-fields.js +24 -0
- package/dist/ops/kept-fields.js.map +1 -0
- package/dist/ops/lineage.d.ts +37 -0
- package/dist/ops/lineage.d.ts.map +1 -0
- package/dist/ops/lineage.js +26 -0
- package/dist/ops/lineage.js.map +1 -0
- package/dist/ops/log.d.ts.map +1 -1
- package/dist/ops/log.js +1 -0
- package/dist/ops/log.js.map +1 -1
- package/dist/ops/provenance.d.ts +68 -0
- package/dist/ops/provenance.d.ts.map +1 -0
- package/dist/ops/provenance.js +83 -0
- package/dist/ops/provenance.js.map +1 -0
- package/dist/ops/types.d.ts +25 -0
- package/dist/ops/types.d.ts.map +1 -1
- package/dist/ops/withheld.d.ts +25 -2
- package/dist/ops/withheld.d.ts.map +1 -1
- package/dist/ops/withheld.js +6 -15
- package/dist/ops/withheld.js.map +1 -1
- package/dist/permissions/policy.d.ts +6 -1
- package/dist/permissions/policy.d.ts.map +1 -1
- package/dist/permissions/policy.js +31 -11
- package/dist/permissions/policy.js.map +1 -1
- package/dist/permissions/sight.d.ts +40 -2
- package/dist/permissions/sight.d.ts.map +1 -1
- package/dist/permissions/sight.js +25 -2
- package/dist/permissions/sight.js.map +1 -1
- package/dist/permissions/types.d.ts +21 -0
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/permissions/types.js.map +1 -1
- package/dist/places.d.ts +0 -49
- package/dist/places.d.ts.map +1 -1
- package/dist/places.js +2 -141
- package/dist/places.js.map +1 -1
- package/dist/provenance.d.ts +14 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +13 -0
- package/dist/provenance.js.map +1 -0
- package/dist/repeat-words.d.ts +13 -0
- package/dist/repeat-words.d.ts.map +1 -0
- package/dist/repeat-words.js +56 -0
- package/dist/repeat-words.js.map +1 -0
- package/dist/repeat.d.ts +59 -0
- package/dist/repeat.d.ts.map +1 -0
- package/dist/repeat.js +155 -0
- package/dist/repeat.js.map +1 -0
- package/dist/scaffold/project.d.ts.map +1 -1
- package/dist/scaffold/project.js +13 -0
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/ui.js +2 -2
- package/dist/scene-districts.d.ts +2 -1
- package/dist/scene-districts.d.ts.map +1 -1
- package/dist/scene-districts.js +2 -1
- package/dist/scene-districts.js.map +1 -1
- package/dist/scene.d.ts +4 -2
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +2 -1
- package/dist/scene.js.map +1 -1
- package/dist/schema/amount-mark.d.ts +13 -0
- package/dist/schema/amount-mark.d.ts.map +1 -0
- package/dist/schema/amount-mark.js +20 -0
- package/dist/schema/amount-mark.js.map +1 -0
- package/dist/schema/amount-words.d.ts +12 -0
- package/dist/schema/amount-words.d.ts.map +1 -0
- package/dist/schema/amount-words.js +31 -0
- package/dist/schema/amount-words.js.map +1 -0
- package/dist/schema/app-link-field.d.ts +4 -0
- package/dist/schema/app-link-field.d.ts.map +1 -0
- package/dist/schema/app-link-field.js +19 -0
- package/dist/schema/app-link-field.js.map +1 -0
- package/dist/schema/app-link.d.ts +41 -0
- package/dist/schema/app-link.d.ts.map +1 -0
- package/dist/schema/app-link.js +40 -0
- package/dist/schema/app-link.js.map +1 -0
- package/dist/schema/define-node.d.ts +25 -94
- package/dist/schema/define-node.d.ts.map +1 -1
- package/dist/schema/define-node.js +3 -247
- package/dist/schema/define-node.js.map +1 -1
- package/dist/schema/field-words.d.ts +45 -0
- package/dist/schema/field-words.d.ts.map +1 -0
- package/dist/schema/field-words.js +124 -0
- package/dist/schema/field-words.js.map +1 -0
- package/dist/schema/json-schema.d.ts +6 -1
- package/dist/schema/json-schema.d.ts.map +1 -1
- package/dist/schema/json-schema.js +83 -2
- package/dist/schema/json-schema.js.map +1 -1
- package/dist/schema/markdown-field.d.ts +4 -0
- package/dist/schema/markdown-field.d.ts.map +1 -0
- package/dist/schema/markdown-field.js +20 -0
- package/dist/schema/markdown-field.js.map +1 -0
- package/dist/schema/markdown.d.ts +6 -0
- package/dist/schema/markdown.d.ts.map +1 -0
- package/dist/schema/markdown.js +27 -0
- package/dist/schema/markdown.js.map +1 -0
- package/dist/schema/reading.d.ts +86 -0
- package/dist/schema/reading.d.ts.map +1 -0
- package/dist/schema/reading.js +270 -0
- package/dist/schema/reading.js.map +1 -0
- package/dist/schema/repeat-field.d.ts +5 -0
- package/dist/schema/repeat-field.d.ts.map +1 -0
- package/dist/schema/repeat-field.js +20 -0
- package/dist/schema/repeat-field.js.map +1 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +5 -1
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/to-json-schema.d.ts +14 -0
- package/dist/schema/to-json-schema.d.ts.map +1 -0
- package/dist/schema/to-json-schema.js +616 -0
- package/dist/schema/to-json-schema.js.map +1 -0
- package/dist/schema/typed-field-builders.d.ts +8 -0
- package/dist/schema/typed-field-builders.d.ts.map +1 -0
- package/dist/schema/typed-field-builders.js +30 -0
- package/dist/schema/typed-field-builders.js.map +1 -0
- package/dist/schema/typed-field-of.d.ts +4 -0
- package/dist/schema/typed-field-of.d.ts.map +1 -0
- package/dist/schema/typed-field-of.js +14 -0
- package/dist/schema/typed-field-of.js.map +1 -0
- package/dist/schema/typed-fields.d.ts +11 -0
- package/dist/schema/typed-fields.d.ts.map +1 -0
- package/dist/schema/typed-fields.js +16 -0
- package/dist/schema/typed-fields.js.map +1 -0
- package/dist/schema/types.d.ts +14 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/value-words.d.ts +21 -0
- package/dist/schema/value-words.d.ts.map +1 -0
- package/dist/schema/value-words.js +32 -0
- package/dist/schema/value-words.js.map +1 -0
- package/dist/search.d.ts +91 -4
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +290 -59
- package/dist/search.js.map +1 -1
- package/dist/seat.d.ts +19 -0
- package/dist/seat.d.ts.map +1 -0
- package/dist/seat.js +19 -0
- package/dist/seat.js.map +1 -0
- package/dist/seen.d.ts +15 -33
- package/dist/seen.d.ts.map +1 -1
- package/dist/seen.js +44 -135
- package/dist/seen.js.map +1 -1
- package/dist/store.d.ts +46 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +121 -22
- package/dist/store.js.map +1 -1
- package/dist/theme/types.d.ts +7 -1
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/dist/typed.d.ts +12 -3
- package/dist/typed.d.ts.map +1 -1
- package/dist/typed.js.map +1 -1
- package/dist/validate-graph.d.ts.map +1 -1
- package/dist/validate-graph.js +3 -10
- package/dist/validate-graph.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/views/types.d.ts +57 -8
- package/dist/views/types.d.ts.map +1 -1
- package/dist/views/types.js +22 -3
- package/dist/views/types.js.map +1 -1
- package/dist/village.d.ts +63 -0
- package/dist/village.d.ts.map +1 -0
- package/dist/village.js +143 -0
- package/dist/village.js.map +1 -0
- package/dist/watched.d.ts +12 -45
- package/dist/watched.d.ts.map +1 -1
- package/dist/watched.js +7 -119
- package/dist/watched.js.map +1 -1
- package/dist/words.d.ts +33 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +36 -0
- package/dist/words.js.map +1 -0
- package/package.json +37 -2
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
import { isArchived } from "../graph/archived.js";
|
|
2
|
+
import { nameKey } from "../labels.js";
|
|
3
|
+
import { mintId, slug } from "../mutations/define-mutation.js";
|
|
4
|
+
import { nodeRefArgs } from "../mutations/node-ref.js";
|
|
5
|
+
import { article, counted, fieldWords, labelOf, nounOf, pluralOf } from "../schema/define-node.js";
|
|
6
|
+
import { edgeWords, valueWords } from "../schema/value-words.js";
|
|
7
|
+
import { actingAs } from "../permissions/policy.js";
|
|
8
|
+
import { FIELD_SIGHT } from "../permissions/sight.js";
|
|
9
|
+
import { seesEveryRecord } from "../seen.js";
|
|
10
|
+
import { problemsMoved } from "../store.js";
|
|
11
|
+
import { andList, coerceCell, orList, splitCell } from "./coerce.js";
|
|
12
|
+
import { ImportMappingError, readMapping, fieldSpecFor } from "./mapping.js";
|
|
13
|
+
/** Cells that mean "nothing here" in a column of numbers, dates or choices. */
|
|
14
|
+
const BLANK = new Set(["n/a", "na", "-", "—", "–", "null", "none", "tbd", "?"]);
|
|
15
|
+
const isBlank = (value) => value === undefined || value === null || (typeof value === "string" && value.trim().length === 0);
|
|
16
|
+
/** Says what a row would do: preview `importRows`. */
|
|
17
|
+
export function previewImport(store, kind, rows, mapping, options) {
|
|
18
|
+
return importRows(store, kind, rows, mapping, { ...options, preview: true });
|
|
19
|
+
}
|
|
20
|
+
export function importRows(store, kind, rows, mapping, options) {
|
|
21
|
+
const schema = store.schema;
|
|
22
|
+
const by = options.by;
|
|
23
|
+
// A field the importer may not see is one it may not fill: its column is refused as one naming no field (Roadmap f-field-sight).
|
|
24
|
+
const columns = readMapping(schema, kind, mapping, FIELD_SIGHT.keptFrom?.(store, by)?.({ id: "\u0000new", kind }));
|
|
25
|
+
const definition = schema.definition(kind);
|
|
26
|
+
const noun = nounOf(definition, kind);
|
|
27
|
+
const preview = options.preview === true;
|
|
28
|
+
const { header, records } = tabulate(rows);
|
|
29
|
+
const absent = columns.filter((column) => !header.includes(column.column)).map((column) => `No column is called ${column.column}.`);
|
|
30
|
+
if (absent.length > 0 && records.length > 0)
|
|
31
|
+
throw new ImportMappingError(absent);
|
|
32
|
+
const ignored = header.filter((column) => !columns.some((one) => one.column === column));
|
|
33
|
+
const forbidden = policyRefusal(store, kind, noun, by);
|
|
34
|
+
/** The importer's own view: what a one-to-one link already holds is judged by what they see (FR-55). */
|
|
35
|
+
const seen = store.seenBy(by);
|
|
36
|
+
const mayLink = linking(store, by);
|
|
37
|
+
const taken = new Set();
|
|
38
|
+
/** The rows planned so far, by their label's key: a later row may link to one. */
|
|
39
|
+
const plannedByName = new Map();
|
|
40
|
+
/** Far records whose one link of a relation this import has already given. */
|
|
41
|
+
const filled = new Set();
|
|
42
|
+
const outcomes = [];
|
|
43
|
+
const planned = [];
|
|
44
|
+
const say = sentences(preview);
|
|
45
|
+
/**
|
|
46
|
+
* The links a row makes from or to the record `id` — a new one, or under
|
|
47
|
+
* `match` one already there (`existing`): a link it already has is no
|
|
48
|
+
* change, and the one link of a relation to one that it holds to another
|
|
49
|
+
* record is replaced — said where the importer sees it, taken quietly
|
|
50
|
+
* where they do not (FR-55).
|
|
51
|
+
*/
|
|
52
|
+
const linksFor = (id, cells, existing) => {
|
|
53
|
+
const reasons = [];
|
|
54
|
+
const links = [];
|
|
55
|
+
const edges = [];
|
|
56
|
+
/** Links a hidden record held that a one-to-one link this row makes takes, as an act's `setSingleSource` takes them (FR-55). */
|
|
57
|
+
const replaced = [];
|
|
58
|
+
/** Links of a relation to one the importer sees, which the row's link replaces. */
|
|
59
|
+
const severed = [];
|
|
60
|
+
for (const column of columns) {
|
|
61
|
+
if (column.as !== "relation")
|
|
62
|
+
continue;
|
|
63
|
+
const raw = cells[column.column];
|
|
64
|
+
if (isBlank(raw))
|
|
65
|
+
continue;
|
|
66
|
+
const text = String(raw).trim();
|
|
67
|
+
const named = column.direction === "out" && column.one ? [text] : namesIn(store, column, text, by);
|
|
68
|
+
if (column.direction === "out" && column.one && named.length > 1) {
|
|
69
|
+
reasons.push(`${column.column} names ${named.length}, and ${article(noun)} ${noun} links to one`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
for (const name of named) {
|
|
73
|
+
const found = resolve(store, column, name, by, plannedByName);
|
|
74
|
+
if (!found.ok) {
|
|
75
|
+
reasons.push(found.why);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (found.id === id) {
|
|
79
|
+
reasons.push(`it names itself under ${column.column}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const edge = column.direction === "out" ? { kind: column.relation, from: id, to: found.id } : { kind: column.relation, from: found.id, to: id };
|
|
83
|
+
if (edges.some((one) => one.kind === edge.kind && one.from === edge.from && one.to === edge.to))
|
|
84
|
+
continue;
|
|
85
|
+
// The record already holds it: nothing to make.
|
|
86
|
+
if (existing && seen.graph.out(edge.from, edge.kind).some((one) => one.id === edge.to))
|
|
87
|
+
continue;
|
|
88
|
+
// Made by the acts that make such a link, as a press on one would be: a grant's kinds, its own end, an agent's `may`.
|
|
89
|
+
const refusal = mayLink(column.relation, found.id, found.kind);
|
|
90
|
+
if (refusal) {
|
|
91
|
+
reasons.push(refusal);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (column.direction === "in" && column.one) {
|
|
95
|
+
const holds = seen.graph.out(found.id, column.relation).length > 0 || filled.has(`${column.relation} ${found.id}`);
|
|
96
|
+
if (holds) {
|
|
97
|
+
reasons.push(`${found.label} already has its one “${edgeWords(schema, found.kind, column.relation, "out")}”`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
// One the importer may not see holds it: taken, and said to nobody, as an act setting a single source takes it.
|
|
101
|
+
replaced.push(...store.graph.outEdges(found.id, column.relation));
|
|
102
|
+
}
|
|
103
|
+
if (existing && column.direction === "out" && column.one) {
|
|
104
|
+
// The one it had goes: said where the importer sees it, taken quietly where they do not.
|
|
105
|
+
for (const held of store.graph.outEdges(id, column.relation))
|
|
106
|
+
(seen.graph.getNode(held.to) ? severed : replaced).push(held);
|
|
107
|
+
}
|
|
108
|
+
edges.push(edge);
|
|
109
|
+
links.push({ relation: column.relation, direction: column.direction, id: found.id, label: found.label });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { reasons, links, edges, replaced, severed };
|
|
113
|
+
};
|
|
114
|
+
const match = options.match === undefined ? undefined : matcher(store, seen, definition, kind, options.match, header, columns, options.dayFirst === true);
|
|
115
|
+
/** Records rows above already change, by id, and the row that does. */
|
|
116
|
+
const changing = new Map();
|
|
117
|
+
const editAct = store.allMutations().find((mutation) => mutation.derived?.kind === kind && mutation.derived.act === "edit");
|
|
118
|
+
const updated = [];
|
|
119
|
+
for (const { row, cells } of records) {
|
|
120
|
+
if (forbidden && !match) {
|
|
121
|
+
outcomes.push({ row, made: false, action: "refuse", links: [], refused: [forbidden], sentence: say.refused(row, [forbidden]) });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const reasons = [];
|
|
125
|
+
const fields = {};
|
|
126
|
+
for (const column of columns) {
|
|
127
|
+
if (column.as !== "field")
|
|
128
|
+
continue;
|
|
129
|
+
const raw = cells[column.column];
|
|
130
|
+
if (isBlank(raw))
|
|
131
|
+
continue;
|
|
132
|
+
const loose = column.spec.type !== "string" && column.spec.type !== "text" && column.spec.type !== "boolean";
|
|
133
|
+
if (loose && typeof raw === "string" && BLANK.has(raw.trim().toLowerCase()))
|
|
134
|
+
continue;
|
|
135
|
+
const read = coerceCell(column.spec, raw, column.column, {
|
|
136
|
+
...(options.dayFirst ? { dayFirst: true } : {}),
|
|
137
|
+
optionWords: (option) => valueWords(definition, column.field, option),
|
|
138
|
+
});
|
|
139
|
+
if (!read.ok) {
|
|
140
|
+
reasons.push(read.why);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const fits = column.schema.safeParse(read.value);
|
|
144
|
+
if (!fits.success) {
|
|
145
|
+
reasons.push(`“${String(raw).trim()}” under ${column.column} does not fit: ${fits.error?.issues?.[0]?.message ?? "the field refuses it"}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
fields[column.field] = fits.data;
|
|
149
|
+
}
|
|
150
|
+
/*
|
|
151
|
+
* THE RECORD THE ROW ALREADY IS, under `match`: found, the row changes
|
|
152
|
+
* it — only what the row has — rather than making a second one.
|
|
153
|
+
*/
|
|
154
|
+
if (match) {
|
|
155
|
+
const found = match(cells, fields, row, plannedByName);
|
|
156
|
+
if (found.why) {
|
|
157
|
+
outcomes.push({ row, made: false, action: "refuse", links: [], refused: [...reasons, found.why], sentence: say.refused(row, [...reasons, found.why]) });
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (found.node) {
|
|
161
|
+
const node = found.node;
|
|
162
|
+
const label = labelOf(definition, node) || node.id;
|
|
163
|
+
const before = changing.get(node.id);
|
|
164
|
+
if (before !== undefined)
|
|
165
|
+
reasons.push(`row ${before} above already changes ${label}`);
|
|
166
|
+
const changes = [];
|
|
167
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
168
|
+
// The value it was found by, written as the sheet spells it, is the same value: no change.
|
|
169
|
+
if (same(node[field], value) || (found.by === field && typeof value === "string" && typeof node[field] === "string" && nameKey(value) === nameKey(node[field])))
|
|
170
|
+
continue;
|
|
171
|
+
changes.push({ field, from: node[field], to: value });
|
|
172
|
+
if (!(editAct?.writes ?? []).includes(field))
|
|
173
|
+
reasons.push(`${fieldWords(definition, field)} is not something “${editAct?.title ?? `Change the ${noun}`}” changes, so a sheet cannot change it`);
|
|
174
|
+
}
|
|
175
|
+
if (reasons.length === 0 && changes.length > 0) {
|
|
176
|
+
try {
|
|
177
|
+
schema.parseNode({ ...node, ...fields });
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
reasons.push(...issuesSaid(definition, error));
|
|
181
|
+
}
|
|
182
|
+
const verdict = store.permits({ name: editAct.name, args: { id: node.id, ...Object.fromEntries(changes.map((change) => [change.field, change.to])) } }, by);
|
|
183
|
+
if (!verdict.ok)
|
|
184
|
+
reasons.push(verdict.refusal.message);
|
|
185
|
+
}
|
|
186
|
+
const linked = linksFor(node.id, cells, true);
|
|
187
|
+
reasons.push(...linked.reasons);
|
|
188
|
+
if (reasons.length > 0) {
|
|
189
|
+
outcomes.push({ row, made: false, action: "refuse", links: [], refused: reasons, sentence: say.refused(row, reasons) });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
changing.set(node.id, row);
|
|
193
|
+
for (const edge of linked.edges)
|
|
194
|
+
if (edge.to === node.id)
|
|
195
|
+
filled.add(`${edge.kind} ${edge.from}`);
|
|
196
|
+
const changed = changes.length > 0 || linked.edges.length > 0;
|
|
197
|
+
if (changed)
|
|
198
|
+
updated.push({ id: node.id, label, changes, links: linked.links, edges: linked.edges, severed: linked.severed, replaced: linked.replaced });
|
|
199
|
+
const words = changes.map((change) => `${fieldWords(definition, change.field)}: ${valueSaid(definition, change.field, change.from)} → ${valueSaid(definition, change.field, change.to)}`);
|
|
200
|
+
outcomes.push({ row, made: false, action: changed ? "change" : "leave", id: node.id, label, fields, changes, links: linked.links, refused: [], sentence: changed ? say.changed(row, label, words, linked.links) : say.left(row, label) });
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (forbidden) {
|
|
204
|
+
outcomes.push({ row, made: false, action: "refuse", links: [], refused: [forbidden], sentence: say.refused(row, [forbidden]) });
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const shape = definition.fields.shape ?? {};
|
|
209
|
+
for (const field of Object.keys(shape)) {
|
|
210
|
+
if (fields[field] !== undefined)
|
|
211
|
+
continue;
|
|
212
|
+
if (definition.defaults && Object.hasOwn(definition.defaults, field)) {
|
|
213
|
+
fields[field] = definition.defaults[field];
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const spec = fieldSpecFor(definition, field)?.spec;
|
|
217
|
+
if (!spec?.required)
|
|
218
|
+
continue;
|
|
219
|
+
const mapped = columns.find((column) => column.as === "field" && column.field === field);
|
|
220
|
+
// A reason of its own only where the cell was blank: a cell that could not be read has said so already.
|
|
221
|
+
if (mapped && !isBlank(cells[mapped.column]))
|
|
222
|
+
continue;
|
|
223
|
+
reasons.push(`it has no ${fieldWords(definition, field)}, which every ${noun} needs`);
|
|
224
|
+
}
|
|
225
|
+
if (reasons.length === 0) {
|
|
226
|
+
try {
|
|
227
|
+
Object.assign(fields, withoutIds(schema.parseNode({ ...fields, id: "import", kind })));
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
reasons.push(...issuesSaid(definition, error));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const label = labelFor(definition, kind, fields, row);
|
|
234
|
+
const id = reasons.length === 0 ? mint(store, seen, by, taken, kind, label) : `import:${row}`;
|
|
235
|
+
const { links, edges, replaced } = (() => {
|
|
236
|
+
const linked = linksFor(id, cells, false);
|
|
237
|
+
reasons.push(...linked.reasons);
|
|
238
|
+
return linked;
|
|
239
|
+
})();
|
|
240
|
+
if (reasons.length > 0) {
|
|
241
|
+
outcomes.push({ row, made: false, action: "refuse", links: [], refused: reasons, sentence: say.refused(row, reasons) });
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
taken.add(id);
|
|
245
|
+
for (const edge of edges)
|
|
246
|
+
if (edge.to === id)
|
|
247
|
+
filled.add(`${edge.kind} ${edge.from}`);
|
|
248
|
+
const key = nameKey(label);
|
|
249
|
+
plannedByName.set(key, [...(plannedByName.get(key) ?? []), { id, kind, label }]);
|
|
250
|
+
planned.push({ node: { ...fields, id, kind }, edges, replaced, label });
|
|
251
|
+
outcomes.push({ row, made: true, action: "make", id, label, fields, links, refused: [], sentence: say.made(row, label, links) });
|
|
252
|
+
}
|
|
253
|
+
const made = planned.length;
|
|
254
|
+
const changed = updated.length;
|
|
255
|
+
const unchanged = outcomes.filter((one) => one.action === "leave").length;
|
|
256
|
+
const linked = [...planned, ...updated].reduce((sum, one) => sum + one.edges.length, 0);
|
|
257
|
+
const refused = outcomes.length - made - changed - unchanged;
|
|
258
|
+
const result = { kind, rows: outcomes, made, changed, unchanged, linked, refused, ignored, sentence: say.totals(outcomes.length, counted(schema, kind, made), linked, refused, match ? { changed, unchanged } : undefined) };
|
|
259
|
+
if (preview || made + changed === 0)
|
|
260
|
+
return { ...result, kept: false };
|
|
261
|
+
const batchIntent = options.intent ?? (changed === 0 ? `Import ${counted(schema, kind, made)}` : made === 0 ? `Change ${counted(schema, kind, changed)} from a sheet` : `Import ${counted(schema, kind, made)} and change ${changed}`);
|
|
262
|
+
const via = options.via ?? "import";
|
|
263
|
+
/** The edits, through the kind's own edit act; and the links a found record gains, beside them in the batch. */
|
|
264
|
+
const edits = updated.filter((one) => one.changes.length > 0).map((one) => ({ name: editAct.name, args: { id: one.id, ...Object.fromEntries(one.changes.map((change) => [change.field, change.to])) } }));
|
|
265
|
+
const relinked = updated.filter((one) => one.edges.length > 0).map((one) => ({
|
|
266
|
+
primitives: [...[...one.severed, ...one.replaced].map((edge) => ({ op: "remove-edge", edge })), ...one.edges.map((edge) => ({ op: "add-edge", edge }))],
|
|
267
|
+
author: options.by,
|
|
268
|
+
intent: `Link ${one.label} to ${andList(one.links.map((link) => link.label))}`,
|
|
269
|
+
batchIntent,
|
|
270
|
+
via,
|
|
271
|
+
}));
|
|
272
|
+
const appended = planned.map((one) => ({
|
|
273
|
+
primitives: [{ op: "add-node", node: one.node }, ...one.edges.map((edge) => ({ op: "add-edge", edge })), ...one.replaced.map((edge) => ({ op: "remove-edge", edge }))],
|
|
274
|
+
author: options.by,
|
|
275
|
+
intent: `Add ${article(noun)} ${noun}: ${one.label}`,
|
|
276
|
+
batchIntent,
|
|
277
|
+
via,
|
|
278
|
+
}));
|
|
279
|
+
const patches = updated.filter((one) => one.changes.length > 0).map((one) => ({
|
|
280
|
+
primitives: [{ op: "patch-node", id: one.id, before: Object.fromEntries(one.changes.map((change) => [change.field, change.from])), after: Object.fromEntries(one.changes.map((change) => [change.field, change.to])) }],
|
|
281
|
+
author: options.by,
|
|
282
|
+
intent: `Change ${one.label}`,
|
|
283
|
+
batchIntent,
|
|
284
|
+
via,
|
|
285
|
+
}));
|
|
286
|
+
if (options.admit) {
|
|
287
|
+
const at = store.log.length;
|
|
288
|
+
const ops = [...patches, ...appended, ...relinked].map((one, index) => ({
|
|
289
|
+
id: `import:${index + 1}`,
|
|
290
|
+
seq: at + index,
|
|
291
|
+
batch: options.batch ?? "import",
|
|
292
|
+
mutation: null,
|
|
293
|
+
inverse: [...one.primitives].reverse(),
|
|
294
|
+
reads: [],
|
|
295
|
+
writes: [...new Set(one.primitives.map((primitive) => ("node" in primitive ? primitive.node.id : primitive.op === "add-edge" ? primitive.edge.from : "")))],
|
|
296
|
+
at: "",
|
|
297
|
+
...one,
|
|
298
|
+
}));
|
|
299
|
+
const size = store.graph.size;
|
|
300
|
+
const taken = planned.reduce((sum, one) => sum + one.replaced.length, 0) + updated.reduce((sum, one) => sum + one.replaced.length + one.severed.length, 0);
|
|
301
|
+
options.admit({
|
|
302
|
+
ops,
|
|
303
|
+
primitives: [...patches, ...appended, ...relinked].flatMap((one) => one.primitives),
|
|
304
|
+
added: { nodes: made, edges: linked },
|
|
305
|
+
removed: { nodes: 0, edges: taken },
|
|
306
|
+
changed: { nodes: edits.length },
|
|
307
|
+
nodesAfter: size.nodes + made,
|
|
308
|
+
edgesAfter: size.edges + linked - taken,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const before = store.violations();
|
|
312
|
+
/*
|
|
313
|
+
* ONE BATCH, ONE UNDO. The edits go through the store's own path — the
|
|
314
|
+
* edit act's checks, its sentence, the policy asked again — and what the
|
|
315
|
+
* rows make and link is appended under the same batch after them.
|
|
316
|
+
*/
|
|
317
|
+
const edited = edits.length > 0 ? store.applyAll(edits, { author: options.by, via, intent: batchIntent, ...(options.batch === undefined ? {} : { batch: options.batch }) }).ops : [];
|
|
318
|
+
const batch = edited[0]?.batch ?? options.batch;
|
|
319
|
+
const rest = [...appended, ...relinked];
|
|
320
|
+
const ops = [...edited, ...(rest.length > 0 ? store.append(rest, batch === undefined ? {} : { batch }) : [])];
|
|
321
|
+
const { introduces } = problemsMoved(store.allInvariants(), before, store.violations());
|
|
322
|
+
return { ...result, kept: true, batch: ops[0].batch, ops, introduces };
|
|
323
|
+
}
|
|
324
|
+
/** The header and the record rows, each numbered as the sheet numbers it, blank rows left out. */
|
|
325
|
+
function tabulate(rows) {
|
|
326
|
+
if (rows.length === 0)
|
|
327
|
+
return { header: [], records: [] };
|
|
328
|
+
if (Array.isArray(rows[0])) {
|
|
329
|
+
const header = rows[0].map((cell) => String(cell ?? "").trim());
|
|
330
|
+
const records = rows.slice(1).flatMap((cells, index) => {
|
|
331
|
+
const list = cells;
|
|
332
|
+
if (list.every(isBlank))
|
|
333
|
+
return [];
|
|
334
|
+
return [{ row: index + 2, cells: Object.fromEntries(header.map((column, at) => [column, list[at]])) }];
|
|
335
|
+
});
|
|
336
|
+
return { header, records };
|
|
337
|
+
}
|
|
338
|
+
const header = [];
|
|
339
|
+
for (const row of rows)
|
|
340
|
+
for (const column of Object.keys(row))
|
|
341
|
+
if (!header.includes(column))
|
|
342
|
+
header.push(column);
|
|
343
|
+
const records = rows.flatMap((cells, index) => (Object.values(cells).every(isBlank) ? [] : [{ row: index + 2, cells: { ...cells } }]));
|
|
344
|
+
return { header, records };
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Why this importer may not add the kind at all, or nothing. Asked of the
|
|
348
|
+
* acts that create it — who may bring one into being by hand may bring
|
|
349
|
+
* one in from a sheet — as the store asks them, the agent's `may` and the
|
|
350
|
+
* policy's grants alike. A kind no act creates is the host's to import
|
|
351
|
+
* where no policy is declared, and nobody's where one is.
|
|
352
|
+
*/
|
|
353
|
+
function policyRefusal(store, kind, noun, by) {
|
|
354
|
+
if (store.modules.disabledKinds.has(kind))
|
|
355
|
+
return `${capital(store.schema.tryDefinition(kind)?.plural ?? `${noun}s`)} belong to a module this workspace has turned off.`;
|
|
356
|
+
const creators = store.allMutations().filter((mutation) => !mutation.derived && (mutation.creates ?? []).includes(kind));
|
|
357
|
+
if (creators.length === 0)
|
|
358
|
+
return store.policy === undefined && by.kind !== "agent" ? undefined : `Nothing in this app adds ${article(noun)} ${noun}, so none can be imported.`;
|
|
359
|
+
let first;
|
|
360
|
+
for (const creator of creators) {
|
|
361
|
+
const verdict = store.permits({ name: creator.name, args: {} }, by);
|
|
362
|
+
if (verdict.ok)
|
|
363
|
+
return undefined;
|
|
364
|
+
first ??= verdict.refusal.message;
|
|
365
|
+
}
|
|
366
|
+
return first;
|
|
367
|
+
}
|
|
368
|
+
const capital = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
369
|
+
/** The names one cell gives: the whole cell when it names a record, else its parts. */
|
|
370
|
+
function namesIn(store, column, text, by) {
|
|
371
|
+
const whole = store.resolveRef({ name: column.column, kinds: column.kinds }, text, by);
|
|
372
|
+
if (whole.ok)
|
|
373
|
+
return [text];
|
|
374
|
+
const parts = splitCell(text);
|
|
375
|
+
return parts.length > 1 ? parts : [text];
|
|
376
|
+
}
|
|
377
|
+
/** A name, resolved as an act resolves it, under the importer's sight; failing that, among the rows this import makes. */
|
|
378
|
+
function resolve(store, column, name, by, planned) {
|
|
379
|
+
const found = store.resolveRef({ name: column.column, kinds: column.kinds }, name, by);
|
|
380
|
+
if (found.ok)
|
|
381
|
+
return { ok: true, id: found.id, label: found.label, kind: String(store.graph.getNode(found.id).kind) };
|
|
382
|
+
if (found.reason === "ambiguous") {
|
|
383
|
+
const labels = [...new Set(found.candidates.map((candidate) => candidate.label))];
|
|
384
|
+
return { ok: false, why: `“${name}” under ${column.column} could be ${labels.length <= 5 ? orList(labels) : `any of ${labels.length} records`}` };
|
|
385
|
+
}
|
|
386
|
+
const here = (planned.get(nameKey(name)) ?? []).filter((one) => column.kinds.includes(one.kind));
|
|
387
|
+
if (here.length === 1)
|
|
388
|
+
return { ok: true, ...here[0] };
|
|
389
|
+
if (here.length > 1)
|
|
390
|
+
return { ok: false, why: `“${name}” under ${column.column} could be any of the ${here.length} rows above it so called` };
|
|
391
|
+
const nouns = [...new Set(column.kinds.map((one) => nounOf(store.schema.tryDefinition(one), one)))];
|
|
392
|
+
return { ok: false, why: `no ${nouns.length <= 3 ? orList(nouns) : "record"} is called “${name}”` };
|
|
393
|
+
}
|
|
394
|
+
/** What the record will be called: its kind's label, else its first words, else the row. */
|
|
395
|
+
function labelFor(definition, kind, fields, row) {
|
|
396
|
+
/*
|
|
397
|
+
* A kind's label reads its own fields — `(node) => node.label`, as every
|
|
398
|
+
* scaffolded kind's does — and a sheet need not hold them: an inspection
|
|
399
|
+
* is named by the act that makes it, not by a column. Said nothing, or
|
|
400
|
+
* thrown, it is named the way a row with no name is (W-196).
|
|
401
|
+
*/
|
|
402
|
+
let said;
|
|
403
|
+
try {
|
|
404
|
+
said = labelOf(definition, { id: "", kind, ...fields });
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
said = undefined;
|
|
408
|
+
}
|
|
409
|
+
if (typeof said === "string" && said.trim().length > 0)
|
|
410
|
+
return said.trim();
|
|
411
|
+
const words = Object.values(fields).find((value) => typeof value === "string" && value.trim().length > 0);
|
|
412
|
+
return words?.trim() ?? `${nounOf(definition, kind)} from row ${row}`;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* An id as the importer's own act's `freshId` would mint it: `<kind>:<label>`,
|
|
416
|
+
* numbered past one they see — and, for a kind they may not see every record
|
|
417
|
+
* of, their own (FR-55), so the id says nothing of a record kept from them.
|
|
418
|
+
*/
|
|
419
|
+
function mint(store, seen, by, taken, kind, label) {
|
|
420
|
+
return mintId(`${kind}:${slug(label)}`, {
|
|
421
|
+
seen: (id) => seen.graph.has(id) || taken.has(id),
|
|
422
|
+
held: (id) => store.graph.has(id) || taken.has(id),
|
|
423
|
+
limited: !seesEveryRecord(store, by)(kind),
|
|
424
|
+
me: actingAs(by).id,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* WHETHER THE IMPORTER MAY MAKE A LINK, asked of the acts that make links of
|
|
429
|
+
* its relation (their `connects`) as a press on one would be: the record at
|
|
430
|
+
* the far end handed to the argument that takes it, so a grant's kinds, a
|
|
431
|
+
* `self` grant, an own-end grant and an agent's `may` all answer as they
|
|
432
|
+
* would. Where no policy is declared a person may link anything, as they may
|
|
433
|
+
* make anything; where one is, a relation no act makes is linked by nobody.
|
|
434
|
+
* Answers the refusal in words, or undefined.
|
|
435
|
+
*/
|
|
436
|
+
function linking(store, by) {
|
|
437
|
+
const asked = new Map();
|
|
438
|
+
return (relation, far, farKind) => {
|
|
439
|
+
const key = `${relation}\u0000${far}`;
|
|
440
|
+
if (asked.has(key))
|
|
441
|
+
return asked.get(key);
|
|
442
|
+
let answer;
|
|
443
|
+
if (store.policy !== undefined || by.kind === "agent") {
|
|
444
|
+
const linkers = store.allMutations().filter((mutation) => !mutation.derived && (mutation.connects ?? []).includes(relation));
|
|
445
|
+
if (linkers.length === 0) {
|
|
446
|
+
if (store.policy !== undefined)
|
|
447
|
+
answer = `nothing in this app links ${linkWords(store, relation, farKind)} that way, so none can be imported`;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
for (const linker of linkers) {
|
|
451
|
+
const subject = linker.subject?.arg;
|
|
452
|
+
const takes = (kinds) => kinds.includes("*") || kinds.includes(farKind);
|
|
453
|
+
const refs = nodeRefArgs(linker.input);
|
|
454
|
+
const at = refs.find((ref) => ref.name !== subject && takes(ref.kinds))?.name ?? (subject !== undefined && refs.some((ref) => ref.name === subject && takes(ref.kinds)) ? subject : undefined);
|
|
455
|
+
const verdict = store.permits({ name: linker.name, args: at === undefined ? {} : { [at]: far } }, by);
|
|
456
|
+
if (verdict.ok) {
|
|
457
|
+
answer = undefined;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
answer ??= verdict.refusal.message;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
asked.set(key, answer);
|
|
465
|
+
return answer;
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
/** "a vendor to a tag": the two ends of a relation, as a sentence says them. */
|
|
469
|
+
function linkWords(store, relation, farKind) {
|
|
470
|
+
const owner = store.schema.definitions.find((definition) => Object.hasOwn((definition.edges ?? {}), relation));
|
|
471
|
+
const far = nounOf(store.schema.tryDefinition(farKind), farKind);
|
|
472
|
+
if (!owner)
|
|
473
|
+
return `anything to ${article(far)} ${far}`;
|
|
474
|
+
const near = nounOf(owner, owner.kind);
|
|
475
|
+
return `${article(near)} ${near} to ${article(far)} ${far}`;
|
|
476
|
+
}
|
|
477
|
+
function withoutIds(node) {
|
|
478
|
+
const { id: _id, kind: _kind, ...fields } = node;
|
|
479
|
+
return fields;
|
|
480
|
+
}
|
|
481
|
+
/** A parse's issues, each by the field's words. */
|
|
482
|
+
function issuesSaid(definition, error) {
|
|
483
|
+
const issues = error.issues;
|
|
484
|
+
if (!issues || issues.length === 0)
|
|
485
|
+
return [error instanceof Error ? error.message : String(error)];
|
|
486
|
+
return issues.map((issue) => {
|
|
487
|
+
const field = typeof issue.path?.[0] === "string" ? issue.path[0] : undefined;
|
|
488
|
+
return field ? `${fieldWords(definition, field)} does not fit: ${issue.message ?? "the field refuses it"}` : (issue.message ?? "it does not fit");
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
function sentences(preview) {
|
|
492
|
+
const rows = (n) => `${n} ${n === 1 ? "row" : "rows"}`;
|
|
493
|
+
const links = (n) => `${n} ${n === 1 ? "link" : "links"}`;
|
|
494
|
+
return {
|
|
495
|
+
made: (row, label, linked) => `Row ${row} ${preview ? "would make" : "made"} ${label}${linked.length > 0 ? `, linked to ${andList(linked.map((link) => link.label))}` : ""}.`,
|
|
496
|
+
changed: (row, label, words, linked) => `Row ${row} ${preview ? "would change" : "changed"} ${label}${words.length > 0 ? ` (${words.join("; ")})` : ""}${linked.length > 0 ? `, ${words.length > 0 ? "and links it" : "linking it"} to ${andList(linked.map((link) => link.label))}` : ""}.`,
|
|
497
|
+
left: (row, label) => `Row ${row} ${preview ? "would leave" : "left"} ${label} as is.`,
|
|
498
|
+
refused: (row, reasons) => `Row ${row} ${preview ? "would be" : "was"} refused: ${reasons.join("; ")}${/[.!?]$/.test(reasons.at(-1) ?? "") ? "" : "."}`,
|
|
499
|
+
totals: (count, made, linked, refused, matched) => `${capital(rows(count))}: ${made} ${preview ? "would be made" : "made"}` +
|
|
500
|
+
(matched && matched.changed > 0 ? `, ${matched.changed} ${preview ? "would be changed" : "changed"}` : "") +
|
|
501
|
+
(matched && matched.unchanged > 0 ? `, ${matched.unchanged} ${preview ? "would be left as is" : "left as is"}` : "") +
|
|
502
|
+
`${linked > 0 ? `, with ${links(linked)}` : ""}${refused > 0 ? `; ${rows(refused)} ${preview ? "would be" : refused === 1 ? "was" : "were"} refused` : ""}.`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/** The same value, as a record holds it and as a row reads it. */
|
|
506
|
+
const same = (a, b) => a === b || JSON.stringify(a) === JSON.stringify(b);
|
|
507
|
+
/** A value as a sentence says it: a choice by its words, nothing as "nothing". */
|
|
508
|
+
function valueSaid(definition, field, value) {
|
|
509
|
+
if (value === undefined || value === null || value === "")
|
|
510
|
+
return "nothing";
|
|
511
|
+
if (typeof value === "string")
|
|
512
|
+
return fieldSpecFor(definition, field)?.spec.type === "enum" ? (valueWords(definition, field, value) ?? value) : value;
|
|
513
|
+
if (Array.isArray(value))
|
|
514
|
+
return value.map((one) => valueSaid(definition, field, one)).join(", ");
|
|
515
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* HOW A ROW FINDS ITS RECORD (`ImportOptions.match`): by what the row would
|
|
519
|
+
* call it, or by one column's value against one field, among the live
|
|
520
|
+
* records of the kind the importer sees. Two that answer are said rather
|
|
521
|
+
* than guessed between; an archived one says so, and is not changed; a row
|
|
522
|
+
* that names one a row above makes is said too, rather than made twice.
|
|
523
|
+
*/
|
|
524
|
+
function matcher(store, seen, definition, kind, match, header, columns, dayFirst) {
|
|
525
|
+
const noun = nounOf(definition, kind);
|
|
526
|
+
const plural = pluralOf(store.schema, kind).toLowerCase();
|
|
527
|
+
if (match !== "label") {
|
|
528
|
+
const problems = [];
|
|
529
|
+
if (!header.includes(match.column))
|
|
530
|
+
problems.push(`No column is called ${match.column}, which the import matches by.`);
|
|
531
|
+
if (!fieldSpecFor(definition, match.field))
|
|
532
|
+
problems.push(`The import matches by “${match.field}”, which ${article(noun)} ${noun} has no field called.`);
|
|
533
|
+
if (problems.length > 0)
|
|
534
|
+
throw new ImportMappingError(problems);
|
|
535
|
+
}
|
|
536
|
+
const records = seen.graph.nodesOfKind(kind, { archived: "all" });
|
|
537
|
+
const keyOf = (value) => (typeof value === "string" ? nameKey(value) : JSON.stringify(value));
|
|
538
|
+
const named = (node) => labelOf(definition, node) || node.id;
|
|
539
|
+
return (cells, fields, row, planned) => {
|
|
540
|
+
let wanted;
|
|
541
|
+
let said;
|
|
542
|
+
if (match === "label") {
|
|
543
|
+
// What the row would call the record: nothing it says, nothing to find.
|
|
544
|
+
if (Object.keys(fields).length === 0)
|
|
545
|
+
return {};
|
|
546
|
+
wanted = labelFor(definition, kind, fields, row);
|
|
547
|
+
said = String(wanted);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
const raw = cells[match.column];
|
|
551
|
+
if (isBlank(raw))
|
|
552
|
+
return {};
|
|
553
|
+
const mapped = columns.find((column) => column.as === "field" && column.column === match.column && column.field === match.field);
|
|
554
|
+
if (mapped) {
|
|
555
|
+
if (fields[match.field] === undefined)
|
|
556
|
+
return {};
|
|
557
|
+
wanted = fields[match.field];
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
const read = coerceCell(fieldSpecFor(definition, match.field).spec, raw, match.column, { ...(dayFirst ? { dayFirst: true } : {}) });
|
|
561
|
+
if (!read.ok)
|
|
562
|
+
return { why: read.why };
|
|
563
|
+
wanted = read.value;
|
|
564
|
+
}
|
|
565
|
+
said = String(raw).trim();
|
|
566
|
+
}
|
|
567
|
+
const key = keyOf(wanted);
|
|
568
|
+
const found = records.filter((node) => keyOf(match === "label" ? named(node) : node[match.field]) === key);
|
|
569
|
+
const live = found.filter((node) => !isArchived(node));
|
|
570
|
+
if (live.length === 1) {
|
|
571
|
+
const node = live[0];
|
|
572
|
+
const by = match === "label" ? Object.keys(fields).find((field) => typeof node[field] === "string" && nameKey(node[field]) === key) : match.field;
|
|
573
|
+
return { node, ...(by ? { by } : {}) };
|
|
574
|
+
}
|
|
575
|
+
if (live.length > 1)
|
|
576
|
+
return { why: `${live.length} ${plural} answer to “${said}”, and a row changes one` };
|
|
577
|
+
if (found.length > 0)
|
|
578
|
+
return { why: `${named(found[0])} is archived; bring it back first, and the row will change it` };
|
|
579
|
+
if (match === "label" && (planned.get(nameKey(said)) ?? []).some((one) => one.kind === kind))
|
|
580
|
+
return { why: `a row above already makes ${said}` };
|
|
581
|
+
return {};
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
//# sourceMappingURL=rows.js.map
|