@arnilo/prism-tools 0.0.0-stage → 0.13.0
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/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/browser/README.md +98 -0
- package/browser/skills/browser-automation/SKILL.md +14 -0
- package/browser/src/actions/cdp.ts +131 -0
- package/browser/src/actions/dialog.ts +25 -0
- package/browser/src/actions/download_release.ts +22 -0
- package/browser/src/actions/index.ts +37 -0
- package/browser/src/actions/locator.ts +70 -0
- package/browser/src/actions/navigate.ts +23 -0
- package/browser/src/actions/screenshot.ts +27 -0
- package/browser/src/actions/scroll.ts +24 -0
- package/browser/src/actions/select_page.ts +21 -0
- package/browser/src/actions/types.ts +114 -0
- package/browser/src/actions/upload.ts +44 -0
- package/browser/src/actions/wait.ts +45 -0
- package/browser/src/browser.ts +197 -0
- package/browser/src/cdp.ts +178 -0
- package/browser/src/checkpoint.ts +189 -0
- package/browser/src/config.ts +72 -0
- package/browser/src/downloads.ts +253 -0
- package/browser/src/errors.ts +31 -0
- package/browser/src/evaluate.ts +101 -0
- package/browser/src/index.ts +129 -0
- package/browser/src/limits.ts +271 -0
- package/browser/src/manager.ts +730 -0
- package/browser/src/network.ts +263 -0
- package/browser/src/observe.ts +178 -0
- package/browser/src/policy.ts +133 -0
- package/browser/src/screenshot.ts +107 -0
- package/browser/src/shared-sandbox.ts +53 -0
- package/browser/src/snapshot.ts +99 -0
- package/browser/src/targets.ts +165 -0
- package/browser/src/tools.ts +480 -0
- package/browser/src/types.ts +316 -0
- package/browser/src/uploads.ts +174 -0
- package/coding/README.md +337 -0
- package/coding/src/agent/acp-operations.ts +146 -0
- package/coding/src/agent/artifacts.ts +38 -0
- package/coding/src/agent/ask-user-decision.ts +623 -0
- package/coding/src/agent/atomic-write.ts +23 -0
- package/coding/src/agent/bounded-file.ts +21 -0
- package/coding/src/agent/checks.ts +301 -0
- package/coding/src/agent/coding-checkpoint.ts +828 -0
- package/coding/src/agent/delete.ts +237 -0
- package/coding/src/agent/diagnostics-tool.ts +163 -0
- package/coding/src/agent/diagnostics.ts +262 -0
- package/coding/src/agent/edit-diff.ts +382 -0
- package/coding/src/agent/edit.ts +322 -0
- package/coding/src/agent/effects.ts +102 -0
- package/coding/src/agent/env.ts +28 -0
- package/coding/src/agent/execution-policy.ts +89 -0
- package/coding/src/agent/file-mutation-queue.ts +72 -0
- package/coding/src/agent/forge/github.ts +603 -0
- package/coding/src/agent/forge/index.ts +16 -0
- package/coding/src/agent/forge/types.ts +185 -0
- package/coding/src/agent/git-aware-repository.ts +305 -0
- package/coding/src/agent/git-exec.ts +326 -0
- package/coding/src/agent/git-status.ts +177 -0
- package/coding/src/agent/git-tools.ts +360 -0
- package/coding/src/agent/git.ts +1074 -0
- package/coding/src/agent/glob-match.ts +146 -0
- package/coding/src/agent/glob.ts +182 -0
- package/coding/src/agent/goal-verify.ts +385 -0
- package/coding/src/agent/grep-worker.ts +74 -0
- package/coding/src/agent/grep.ts +459 -0
- package/coding/src/agent/identity.ts +48 -0
- package/coding/src/agent/index.ts +676 -0
- package/coding/src/agent/language/client.ts +429 -0
- package/coding/src/agent/language/framing.ts +190 -0
- package/coding/src/agent/language/index.ts +23 -0
- package/coding/src/agent/language/intelligence.ts +682 -0
- package/coding/src/agent/language/types.ts +187 -0
- package/coding/src/agent/lifecycle-events.ts +121 -0
- package/coding/src/agent/lifecycle.ts +166 -0
- package/coding/src/agent/limits.ts +214 -0
- package/coding/src/agent/move.ts +178 -0
- package/coding/src/agent/mutation-path.ts +52 -0
- package/coding/src/agent/output-accumulator.ts +304 -0
- package/coding/src/agent/path-utils.ts +120 -0
- package/coding/src/agent/process/index.ts +58 -0
- package/coding/src/agent/process/recovery.ts +508 -0
- package/coding/src/agent/process/sessions-host.ts +222 -0
- package/coding/src/agent/process/sessions-monitor.ts +141 -0
- package/coding/src/agent/process/sessions-recovery.ts +241 -0
- package/coding/src/agent/process/sessions-spawn.ts +613 -0
- package/coding/src/agent/process/sessions-teardown.ts +281 -0
- package/coding/src/agent/process/sessions.ts +30 -0
- package/coding/src/agent/process/types.ts +358 -0
- package/coding/src/agent/read-path-set.ts +126 -0
- package/coding/src/agent/read.ts +713 -0
- package/coding/src/agent/repository/glob.ts +147 -0
- package/coding/src/agent/repository/indexed-search.ts +480 -0
- package/coding/src/agent/repository/list.ts +131 -0
- package/coding/src/agent/repository/operations.ts +22 -0
- package/coding/src/agent/repository/path.ts +98 -0
- package/coding/src/agent/repository/search.ts +277 -0
- package/coding/src/agent/repository/types.ts +243 -0
- package/coding/src/agent/repository/walk.ts +126 -0
- package/coding/src/agent/repository.ts +12 -0
- package/coding/src/agent/shell-background.ts +264 -0
- package/coding/src/agent/shell-output.ts +75 -0
- package/coding/src/agent/shell.ts +526 -0
- package/coding/src/agent/todo.ts +399 -0
- package/coding/src/agent/truncate.ts +251 -0
- package/coding/src/agent/write.ts +170 -0
- package/coding/src/ask-user-host.ts +48 -0
- package/coding/src/config.ts +278 -0
- package/coding/src/hard-deny.ts +97 -0
- package/coding/src/index.ts +318 -0
- package/coding/src/principal-guard.ts +84 -0
- package/coding/src/sandbox.ts +92 -0
- package/coding/src/security/approval.ts +144 -0
- package/coding/src/security/command-rules.ts +57 -0
- package/coding/src/security/docker-cli.ts +222 -0
- package/coding/src/security/docker-sandbox.ts +1205 -0
- package/coding/src/security/e2b-sandbox.ts +853 -0
- package/coding/src/security/egress/dns-pin.ts +88 -0
- package/coding/src/security/egress/index.ts +32 -0
- package/coding/src/security/egress/limits.ts +57 -0
- package/coding/src/security/egress/policy.ts +137 -0
- package/coding/src/security/egress/proxy.ts +599 -0
- package/coding/src/security/egress/types.ts +30 -0
- package/coding/src/security/index.ts +199 -0
- package/coding/src/security/native-sandbox.ts +639 -0
- package/coding/src/security/path-containment.ts +55 -0
- package/coding/src/security/sandbox-coding-operations.ts +392 -0
- package/coding/src/security/sandbox-fs-operations.ts +644 -0
- package/coding/src/security/sandbox-limits.ts +147 -0
- package/coding/src/security/sandbox-tar.ts +308 -0
- package/coding/src/security/sandbox.ts +179 -0
- package/coding/src/security/semaphore.ts +55 -0
- package/coding/src/targets.ts +110 -0
- package/coding/src/workspace/diff.ts +78 -0
- package/coding/src/workspace/errors.ts +28 -0
- package/coding/src/workspace/index.ts +5 -0
- package/coding/src/workspace/merge.ts +151 -0
- package/coding/src/workspace/routing.ts +76 -0
- package/coding/src/workspace/service.ts +545 -0
- package/computer-use/README.md +105 -0
- package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
- package/computer-use/src/classify.ts +53 -0
- package/computer-use/src/command.ts +29 -0
- package/computer-use/src/config.ts +69 -0
- package/computer-use/src/create.ts +170 -0
- package/computer-use/src/devices.ts +247 -0
- package/computer-use/src/env.ts +28 -0
- package/computer-use/src/index.ts +157 -0
- package/computer-use/src/skill.ts +21 -0
- package/openapi/README.md +97 -0
- package/openapi/src/compile.ts +386 -0
- package/openapi/src/config.ts +73 -0
- package/openapi/src/errors.ts +16 -0
- package/openapi/src/execute.ts +241 -0
- package/openapi/src/index.ts +124 -0
- package/openapi/src/limits.ts +63 -0
- package/openapi/src/specs.ts +86 -0
- package/openapi/src/tools.ts +60 -0
- package/openapi/src/types.ts +61 -0
- package/package.json +171 -4
- package/research/README.md +72 -0
- package/research/skills/research-report/SKILL.md +12 -0
- package/research/skills/research-scoping/SKILL.md +10 -0
- package/research/skills/research-synthesis/SKILL.md +10 -0
- package/research/skills/source-triage/SKILL.md +10 -0
- package/research/src/check.ts +71 -0
- package/research/src/config.ts +41 -0
- package/research/src/index.ts +147 -0
- package/research/src/ledger.ts +157 -0
- package/research/src/urls.ts +21 -0
- package/web/README.md +287 -0
- package/web/skills/web-research/SKILL.md +17 -0
- package/web/src/brave.ts +66 -0
- package/web/src/cache.ts +151 -0
- package/web/src/config.ts +210 -0
- package/web/src/controls.ts +128 -0
- package/web/src/domains.ts +114 -0
- package/web/src/exa.ts +60 -0
- package/web/src/firecrawl.ts +160 -0
- package/web/src/index.ts +387 -0
- package/web/src/kagi.ts +73 -0
- package/web/src/limits.ts +55 -0
- package/web/src/native/convert.ts +84 -0
- package/web/src/native/fetch.ts +226 -0
- package/web/src/normalize.ts +131 -0
- package/web/src/obscura/cdp.ts +201 -0
- package/web/src/obscura/classify.ts +35 -0
- package/web/src/obscura/cli.ts +254 -0
- package/web/src/obscura/errors.ts +8 -0
- package/web/src/obscura/index.ts +59 -0
- package/web/src/obscura/limits.ts +92 -0
- package/web/src/obscura/mcp.ts +104 -0
- package/web/src/obscura/playwright.ts +133 -0
- package/web/src/obscura/process.ts +235 -0
- package/web/src/obscura/search-profile.ts +73 -0
- package/web/src/obscura/types.ts +48 -0
- package/web/src/obscura/web.ts +419 -0
- package/web/src/prompt-fetch.ts +88 -0
- package/web/src/render.ts +75 -0
- package/web/src/searxng.ts +78 -0
- package/web/src/slice.ts +139 -0
- package/web/src/tavily.ts +77 -0
- package/web/src/tools.ts +182 -0
- package/web/src/transport.ts +310 -0
- package/web/src/types.ts +199 -0
- package/work/README.md +77 -0
- package/work/THIRD_PARTY_NOTICES +24 -0
- package/work/docling/ocr.py +150 -0
- package/work/sandbox/Dockerfile +36 -0
- package/work/sandbox/soffice.sh +12 -0
- package/work/src/command.ts +29 -0
- package/work/src/config.ts +138 -0
- package/work/src/connectors/cli.ts +343 -0
- package/work/src/connectors/drafts.ts +647 -0
- package/work/src/connectors/errors.ts +8 -0
- package/work/src/connectors/file-bytes.ts +149 -0
- package/work/src/connectors/google-workspace-http.ts +399 -0
- package/work/src/connectors/google-workspace.ts +465 -0
- package/work/src/connectors/gws-tools.ts +378 -0
- package/work/src/connectors/http.ts +123 -0
- package/work/src/connectors/idempotency.ts +235 -0
- package/work/src/connectors/identity.ts +34 -0
- package/work/src/connectors/index.ts +93 -0
- package/work/src/connectors/limits.ts +50 -0
- package/work/src/connectors/m365-tools.ts +308 -0
- package/work/src/connectors/microsoft365-http.ts +428 -0
- package/work/src/connectors/microsoft365.ts +402 -0
- package/work/src/connectors/normalize.ts +120 -0
- package/work/src/connectors/tool-helpers.ts +398 -0
- package/work/src/connectors/tools.ts +42 -0
- package/work/src/connectors/types.ts +399 -0
- package/work/src/diagrams/canonicalize.ts +166 -0
- package/work/src/diagrams/caps.ts +52 -0
- package/work/src/diagrams/embed.ts +340 -0
- package/work/src/diagrams/errors.ts +76 -0
- package/work/src/diagrams/index.ts +81 -0
- package/work/src/diagrams/messages.ts +150 -0
- package/work/src/diagrams/origin.ts +52 -0
- package/work/src/diagrams/telemetry.ts +34 -0
- package/work/src/diagrams/xml.ts +267 -0
- package/work/src/document-extraction/index.ts +563 -0
- package/work/src/document-reader/ambient.d.ts +2 -0
- package/work/src/document-reader/errors.ts +7 -0
- package/work/src/document-reader/index.ts +181 -0
- package/work/src/document-reader/mistral-ocr.ts +318 -0
- package/work/src/document-reader/parsers.ts +171 -0
- package/work/src/documents/caps.ts +135 -0
- package/work/src/documents/diff.ts +269 -0
- package/work/src/documents/errors.ts +44 -0
- package/work/src/documents/fidelity.ts +98 -0
- package/work/src/documents/generate.ts +96 -0
- package/work/src/documents/hash.ts +8 -0
- package/work/src/documents/index.ts +14 -0
- package/work/src/documents/model-schema.ts +639 -0
- package/work/src/documents/parse.ts +252 -0
- package/work/src/documents/patch-history.ts +81 -0
- package/work/src/documents/patch.ts +476 -0
- package/work/src/documents/preview-html.ts +245 -0
- package/work/src/documents/preview.ts +227 -0
- package/work/src/documents/telemetry.ts +36 -0
- package/work/src/documents/translate/docx.ts +292 -0
- package/work/src/documents/translate/pptx.ts +304 -0
- package/work/src/documents/translate/xlsx.ts +137 -0
- package/work/src/documents/types.ts +188 -0
- package/work/src/index.ts +311 -0
- package/work/src/runtime/worker-pool-entry.ts +102 -0
- package/work/src/runtime/worker-pool.ts +267 -0
- package/work/src/sandbox/composition.ts +212 -0
- package/work/src/sandbox/image.ts +6 -0
- package/work/src/sandbox/index.ts +14 -0
- package/work/src/sheets/caps.ts +68 -0
- package/work/src/sheets/csv.ts +351 -0
- package/work/src/sheets/decimal.ts +388 -0
- package/work/src/sheets/errors.ts +52 -0
- package/work/src/sheets/index.ts +62 -0
- package/work/src/sheets/inference.ts +378 -0
- package/work/src/sheets/telemetry.ts +34 -0
- package/work/src/sheets/types.ts +162 -0
- package/work/src/sheets/xlsx.ts +388 -0
- package/work/src/skills/index.ts +8 -0
- package/work/src/skills/load.ts +43 -0
- package/work/src/tools/filesystem.ts +21 -0
- package/work/src/tools/index.ts +1 -0
- package/work/src/tools/local-filesystem.ts +33 -0
- package/work/src/tools/office.ts +311 -0
- package/work/vendor/hermes-agent/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
- package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
- package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
- package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
- package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
- package/work/vendor-lock.json +11 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import { Ajv, type ValidateFunction } from "ajv";
|
|
2
|
+
import { DocumentsValidationError } from "./errors.js";
|
|
3
|
+
import type { DocModel, DocumentKind, DocumentModel, SheetModel, TableBlock } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export type JsonSchema = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
// --- Draft-07 Shared Block and Cell Definitions ---
|
|
8
|
+
|
|
9
|
+
const SHARED_DEFS: Record<string, JsonSchema> = {
|
|
10
|
+
DocRun: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
text: { type: "string" },
|
|
14
|
+
bold: { type: "boolean" },
|
|
15
|
+
italic: { type: "boolean" },
|
|
16
|
+
underline: { type: "boolean" },
|
|
17
|
+
strikethrough: { type: "boolean" },
|
|
18
|
+
code: { type: "boolean" },
|
|
19
|
+
link: { type: "string", maxLength: 2048 },
|
|
20
|
+
},
|
|
21
|
+
required: ["text"],
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
ListItem: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
text: { type: "string" },
|
|
28
|
+
runs: {
|
|
29
|
+
type: "array",
|
|
30
|
+
items: { $ref: "#/$defs/DocRun" },
|
|
31
|
+
maxItems: 1000,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["text"],
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
DecimalCellValue: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
type: { const: "decimal" },
|
|
41
|
+
value: {
|
|
42
|
+
type: "string",
|
|
43
|
+
pattern: "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$",
|
|
44
|
+
maxLength: 128,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: ["type", "value"],
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
DateCellValue: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
type: { const: "date" },
|
|
54
|
+
value: {
|
|
55
|
+
type: "string",
|
|
56
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ["type", "value"],
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
},
|
|
62
|
+
DateTimeCellValue: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
type: { const: "datetime" },
|
|
66
|
+
value: {
|
|
67
|
+
type: "string",
|
|
68
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ["type", "value"],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
FormulaCellValue: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
formula: { type: "string", pattern: "^=.*$", maxLength: 4096 },
|
|
78
|
+
cachedValue: {
|
|
79
|
+
type: ["string", "number", "boolean", "null"],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
required: ["formula"],
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
CellValue: {
|
|
86
|
+
anyOf: [
|
|
87
|
+
{ type: "string", maxLength: 100000 },
|
|
88
|
+
{ type: "number" },
|
|
89
|
+
{ type: "boolean" },
|
|
90
|
+
{ type: "null" },
|
|
91
|
+
{ $ref: "#/$defs/DecimalCellValue" },
|
|
92
|
+
{ $ref: "#/$defs/DateCellValue" },
|
|
93
|
+
{ $ref: "#/$defs/DateTimeCellValue" },
|
|
94
|
+
{ $ref: "#/$defs/FormulaCellValue" },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
ChartSeries: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
name: { type: "string", maxLength: 256 },
|
|
101
|
+
values: {
|
|
102
|
+
type: "array",
|
|
103
|
+
items: { type: "number" },
|
|
104
|
+
maxItems: 10000,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
required: ["name", "values"],
|
|
108
|
+
additionalProperties: false,
|
|
109
|
+
},
|
|
110
|
+
ChartData: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
categories: {
|
|
114
|
+
type: "array",
|
|
115
|
+
items: { type: "string", maxLength: 256 },
|
|
116
|
+
maxItems: 10000,
|
|
117
|
+
},
|
|
118
|
+
series: {
|
|
119
|
+
type: "array",
|
|
120
|
+
items: { $ref: "#/$defs/ChartSeries" },
|
|
121
|
+
maxItems: 100,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ["categories", "series"],
|
|
125
|
+
additionalProperties: false,
|
|
126
|
+
},
|
|
127
|
+
HeadingBlock: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
type: { const: "heading" },
|
|
131
|
+
level: { type: "integer", minimum: 1, maximum: 6 },
|
|
132
|
+
text: { type: "string", maxLength: 10000 },
|
|
133
|
+
id: { type: "string", maxLength: 256 },
|
|
134
|
+
},
|
|
135
|
+
required: ["type", "level", "text"],
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
},
|
|
138
|
+
ParagraphBlock: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
type: { const: "paragraph" },
|
|
142
|
+
text: { type: "string", maxLength: 100000 },
|
|
143
|
+
runs: {
|
|
144
|
+
type: "array",
|
|
145
|
+
items: { $ref: "#/$defs/DocRun" },
|
|
146
|
+
maxItems: 5000,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
required: ["type"],
|
|
150
|
+
additionalProperties: false,
|
|
151
|
+
},
|
|
152
|
+
ListBlock: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
type: { const: "list" },
|
|
156
|
+
ordered: { type: "boolean" },
|
|
157
|
+
items: {
|
|
158
|
+
type: "array",
|
|
159
|
+
items: {
|
|
160
|
+
anyOf: [{ type: "string", maxLength: 10000 }, { $ref: "#/$defs/ListItem" }],
|
|
161
|
+
},
|
|
162
|
+
maxItems: 10000,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
required: ["type", "items"],
|
|
166
|
+
additionalProperties: false,
|
|
167
|
+
},
|
|
168
|
+
TableBlock: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
type: { const: "table" },
|
|
172
|
+
rows: { type: "integer", minimum: 0, maximum: 100000 },
|
|
173
|
+
columns: { type: "integer", minimum: 0, maximum: 1000 },
|
|
174
|
+
headers: {
|
|
175
|
+
type: "array",
|
|
176
|
+
items: { type: "string", maxLength: 1000 },
|
|
177
|
+
maxItems: 1000,
|
|
178
|
+
},
|
|
179
|
+
cells: {
|
|
180
|
+
type: "array",
|
|
181
|
+
items: {
|
|
182
|
+
type: "array",
|
|
183
|
+
items: { $ref: "#/$defs/CellValue" },
|
|
184
|
+
maxItems: 1000,
|
|
185
|
+
},
|
|
186
|
+
maxItems: 100000,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
required: ["type", "rows", "columns", "cells"],
|
|
190
|
+
additionalProperties: false,
|
|
191
|
+
},
|
|
192
|
+
ImageBlock: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {
|
|
195
|
+
type: { const: "image" },
|
|
196
|
+
data: { type: "string" },
|
|
197
|
+
mimeType: { type: "string", maxLength: 128 },
|
|
198
|
+
alt: { type: "string", maxLength: 1024 },
|
|
199
|
+
width: { type: "number", minimum: 0 },
|
|
200
|
+
height: { type: "number", minimum: 0 },
|
|
201
|
+
ref: { type: "string", maxLength: 1024 },
|
|
202
|
+
},
|
|
203
|
+
required: ["type"],
|
|
204
|
+
additionalProperties: false,
|
|
205
|
+
},
|
|
206
|
+
PageBreakBlock: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
type: { const: "page-break" },
|
|
210
|
+
},
|
|
211
|
+
required: ["type"],
|
|
212
|
+
additionalProperties: false,
|
|
213
|
+
},
|
|
214
|
+
ChartBlock: {
|
|
215
|
+
type: "object",
|
|
216
|
+
properties: {
|
|
217
|
+
type: { const: "chart" },
|
|
218
|
+
chartType: { type: "string", enum: ["bar", "line", "pie", "scatter"] },
|
|
219
|
+
title: { type: "string", maxLength: 512 },
|
|
220
|
+
data: { $ref: "#/$defs/ChartData" },
|
|
221
|
+
alt: { type: "string", maxLength: 1024 },
|
|
222
|
+
width: { type: "number", minimum: 0 },
|
|
223
|
+
height: { type: "number", minimum: 0 },
|
|
224
|
+
},
|
|
225
|
+
required: ["type", "chartType", "data"],
|
|
226
|
+
additionalProperties: false,
|
|
227
|
+
},
|
|
228
|
+
DocBlock: {
|
|
229
|
+
anyOf: [
|
|
230
|
+
{ $ref: "#/$defs/HeadingBlock" },
|
|
231
|
+
{ $ref: "#/$defs/ParagraphBlock" },
|
|
232
|
+
{ $ref: "#/$defs/ListBlock" },
|
|
233
|
+
{ $ref: "#/$defs/TableBlock" },
|
|
234
|
+
{ $ref: "#/$defs/ImageBlock" },
|
|
235
|
+
{ $ref: "#/$defs/PageBreakBlock" },
|
|
236
|
+
{ $ref: "#/$defs/ChartBlock" },
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
ColumnWidth: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {
|
|
242
|
+
column: { type: "integer", minimum: 0 },
|
|
243
|
+
width: { type: "number", minimum: 0 },
|
|
244
|
+
},
|
|
245
|
+
required: ["column", "width"],
|
|
246
|
+
additionalProperties: false,
|
|
247
|
+
},
|
|
248
|
+
FrozenPanes: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
rows: { type: "integer", minimum: 0 },
|
|
252
|
+
columns: { type: "integer", minimum: 0 },
|
|
253
|
+
},
|
|
254
|
+
additionalProperties: false,
|
|
255
|
+
},
|
|
256
|
+
SheetData: {
|
|
257
|
+
type: "object",
|
|
258
|
+
properties: {
|
|
259
|
+
name: { type: "string", minLength: 1, maxLength: 256 },
|
|
260
|
+
cells: {
|
|
261
|
+
type: "array",
|
|
262
|
+
items: {
|
|
263
|
+
type: "array",
|
|
264
|
+
items: { $ref: "#/$defs/CellValue" },
|
|
265
|
+
maxItems: 16384,
|
|
266
|
+
},
|
|
267
|
+
maxItems: 1048576,
|
|
268
|
+
},
|
|
269
|
+
columnWidths: {
|
|
270
|
+
type: "array",
|
|
271
|
+
items: { $ref: "#/$defs/ColumnWidth" },
|
|
272
|
+
maxItems: 16384,
|
|
273
|
+
},
|
|
274
|
+
frozenPanes: { $ref: "#/$defs/FrozenPanes" },
|
|
275
|
+
numberFormats: {
|
|
276
|
+
type: "object",
|
|
277
|
+
additionalProperties: { type: "string", maxLength: 256 },
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
required: ["name", "cells"],
|
|
281
|
+
additionalProperties: false,
|
|
282
|
+
},
|
|
283
|
+
SlideLayout: {
|
|
284
|
+
type: "string",
|
|
285
|
+
enum: ["title", "title-and-content", "section-header", "two-column", "blank"],
|
|
286
|
+
},
|
|
287
|
+
SlideData: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
layout: { $ref: "#/$defs/SlideLayout" },
|
|
291
|
+
title: { type: "string", maxLength: 1024 },
|
|
292
|
+
subtitle: { type: "string", maxLength: 1024 },
|
|
293
|
+
bullets: {
|
|
294
|
+
type: "array",
|
|
295
|
+
items: {
|
|
296
|
+
anyOf: [{ type: "string", maxLength: 10000 }, { $ref: "#/$defs/ListItem" }],
|
|
297
|
+
},
|
|
298
|
+
maxItems: 1000,
|
|
299
|
+
},
|
|
300
|
+
notes: { type: "string", maxLength: 100000 },
|
|
301
|
+
image: { $ref: "#/$defs/ImageBlock" },
|
|
302
|
+
chart: { $ref: "#/$defs/ChartBlock" },
|
|
303
|
+
},
|
|
304
|
+
required: ["layout"],
|
|
305
|
+
additionalProperties: false,
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// --- Per-Kind Schema Definitions ---
|
|
310
|
+
|
|
311
|
+
export const docModelSchema: JsonSchema = {
|
|
312
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
313
|
+
title: "Prism Document Model (doc)",
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
kind: { const: "doc" },
|
|
317
|
+
modelVersion: { type: "integer", minimum: 1 },
|
|
318
|
+
title: { type: "string", maxLength: 1000 },
|
|
319
|
+
metadata: { type: "object", additionalProperties: true },
|
|
320
|
+
blocks: {
|
|
321
|
+
type: "array",
|
|
322
|
+
items: { $ref: "#/$defs/DocBlock" },
|
|
323
|
+
maxItems: 50000,
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
required: ["kind", "modelVersion", "blocks"],
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
$defs: {
|
|
329
|
+
DocRun: SHARED_DEFS.DocRun,
|
|
330
|
+
ListItem: SHARED_DEFS.ListItem,
|
|
331
|
+
DecimalCellValue: SHARED_DEFS.DecimalCellValue,
|
|
332
|
+
DateCellValue: SHARED_DEFS.DateCellValue,
|
|
333
|
+
DateTimeCellValue: SHARED_DEFS.DateTimeCellValue,
|
|
334
|
+
FormulaCellValue: SHARED_DEFS.FormulaCellValue,
|
|
335
|
+
CellValue: SHARED_DEFS.CellValue,
|
|
336
|
+
HeadingBlock: SHARED_DEFS.HeadingBlock,
|
|
337
|
+
ParagraphBlock: SHARED_DEFS.ParagraphBlock,
|
|
338
|
+
ListBlock: SHARED_DEFS.ListBlock,
|
|
339
|
+
TableBlock: SHARED_DEFS.TableBlock,
|
|
340
|
+
ImageBlock: SHARED_DEFS.ImageBlock,
|
|
341
|
+
PageBreakBlock: SHARED_DEFS.PageBreakBlock,
|
|
342
|
+
ChartSeries: SHARED_DEFS.ChartSeries,
|
|
343
|
+
ChartData: SHARED_DEFS.ChartData,
|
|
344
|
+
ChartBlock: SHARED_DEFS.ChartBlock,
|
|
345
|
+
DocBlock: SHARED_DEFS.DocBlock,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export const sheetModelSchema: JsonSchema = {
|
|
350
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
351
|
+
title: "Prism Sheet Model (sheet)",
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {
|
|
354
|
+
kind: { const: "sheet" },
|
|
355
|
+
modelVersion: { type: "integer", minimum: 1 },
|
|
356
|
+
title: { type: "string", maxLength: 1000 },
|
|
357
|
+
metadata: { type: "object", additionalProperties: true },
|
|
358
|
+
sheets: {
|
|
359
|
+
type: "array",
|
|
360
|
+
items: { $ref: "#/$defs/SheetData" },
|
|
361
|
+
maxItems: 1000,
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
required: ["kind", "modelVersion", "sheets"],
|
|
365
|
+
additionalProperties: false,
|
|
366
|
+
$defs: {
|
|
367
|
+
DecimalCellValue: SHARED_DEFS.DecimalCellValue,
|
|
368
|
+
DateCellValue: SHARED_DEFS.DateCellValue,
|
|
369
|
+
DateTimeCellValue: SHARED_DEFS.DateTimeCellValue,
|
|
370
|
+
FormulaCellValue: SHARED_DEFS.FormulaCellValue,
|
|
371
|
+
CellValue: SHARED_DEFS.CellValue,
|
|
372
|
+
ColumnWidth: SHARED_DEFS.ColumnWidth,
|
|
373
|
+
FrozenPanes: SHARED_DEFS.FrozenPanes,
|
|
374
|
+
SheetData: SHARED_DEFS.SheetData,
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
export const deckModelSchema: JsonSchema = {
|
|
379
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
380
|
+
title: "Prism Deck Model (deck)",
|
|
381
|
+
type: "object",
|
|
382
|
+
properties: {
|
|
383
|
+
kind: { const: "deck" },
|
|
384
|
+
modelVersion: { type: "integer", minimum: 1 },
|
|
385
|
+
title: { type: "string", maxLength: 1000 },
|
|
386
|
+
metadata: { type: "object", additionalProperties: true },
|
|
387
|
+
slides: {
|
|
388
|
+
type: "array",
|
|
389
|
+
items: { $ref: "#/$defs/SlideData" },
|
|
390
|
+
maxItems: 2000,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
required: ["kind", "modelVersion", "slides"],
|
|
394
|
+
additionalProperties: false,
|
|
395
|
+
$defs: {
|
|
396
|
+
DocRun: SHARED_DEFS.DocRun,
|
|
397
|
+
ListItem: SHARED_DEFS.ListItem,
|
|
398
|
+
ImageBlock: SHARED_DEFS.ImageBlock,
|
|
399
|
+
ChartSeries: SHARED_DEFS.ChartSeries,
|
|
400
|
+
ChartData: SHARED_DEFS.ChartData,
|
|
401
|
+
ChartBlock: SHARED_DEFS.ChartBlock,
|
|
402
|
+
SlideLayout: SHARED_DEFS.SlideLayout,
|
|
403
|
+
SlideData: SHARED_DEFS.SlideData,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const SCHEMAS_BY_KIND: Record<DocumentKind, JsonSchema> = {
|
|
408
|
+
doc: docModelSchema,
|
|
409
|
+
sheet: sheetModelSchema,
|
|
410
|
+
deck: deckModelSchema,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// --- Ajv Compilation and Caching ---
|
|
414
|
+
|
|
415
|
+
const ajv = new Ajv({
|
|
416
|
+
allErrors: true,
|
|
417
|
+
validateSchema: true,
|
|
418
|
+
allowUnionTypes: true,
|
|
419
|
+
addUsedSchema: false,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const compiledValidators = new Map<DocumentKind, ValidateFunction>();
|
|
423
|
+
|
|
424
|
+
function getValidator(kind: DocumentKind): ValidateFunction {
|
|
425
|
+
let validator = compiledValidators.get(kind);
|
|
426
|
+
if (!validator) {
|
|
427
|
+
const schema = SCHEMAS_BY_KIND[kind];
|
|
428
|
+
validator = ajv.compile(schema);
|
|
429
|
+
compiledValidators.set(kind, validator);
|
|
430
|
+
}
|
|
431
|
+
return validator;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// --- Slice Key Aliases ---
|
|
435
|
+
|
|
436
|
+
const ALIAS_MAP: Record<string, string> = {
|
|
437
|
+
"doc.heading": "HeadingBlock",
|
|
438
|
+
heading: "HeadingBlock",
|
|
439
|
+
"doc.paragraph": "ParagraphBlock",
|
|
440
|
+
paragraph: "ParagraphBlock",
|
|
441
|
+
"doc.list": "ListBlock",
|
|
442
|
+
list: "ListBlock",
|
|
443
|
+
"doc.table": "TableBlock",
|
|
444
|
+
table: "TableBlock",
|
|
445
|
+
"doc.image": "ImageBlock",
|
|
446
|
+
image: "ImageBlock",
|
|
447
|
+
"doc.page-break": "PageBreakBlock",
|
|
448
|
+
"doc.pagebreak": "PageBreakBlock",
|
|
449
|
+
pagebreak: "PageBreakBlock",
|
|
450
|
+
"page-break": "PageBreakBlock",
|
|
451
|
+
"doc.chart": "ChartBlock",
|
|
452
|
+
chart: "ChartBlock",
|
|
453
|
+
"doc.run": "DocRun",
|
|
454
|
+
run: "DocRun",
|
|
455
|
+
"doc.block": "DocBlock",
|
|
456
|
+
block: "DocBlock",
|
|
457
|
+
"sheet.sheet": "SheetData",
|
|
458
|
+
"sheet.sheetdata": "SheetData",
|
|
459
|
+
sheetdata: "SheetData",
|
|
460
|
+
"sheet.cell": "CellValue",
|
|
461
|
+
cell: "CellValue",
|
|
462
|
+
cellvalue: "CellValue",
|
|
463
|
+
decimal: "DecimalCellValue",
|
|
464
|
+
date: "DateCellValue",
|
|
465
|
+
datetime: "DateTimeCellValue",
|
|
466
|
+
formula: "FormulaCellValue",
|
|
467
|
+
"deck.slide": "SlideData",
|
|
468
|
+
slide: "SlideData",
|
|
469
|
+
slidedata: "SlideData",
|
|
470
|
+
"deck.layout": "SlideLayout",
|
|
471
|
+
layout: "SlideLayout",
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
function resolveDefKey(schemaDefs: Record<string, JsonSchema>, sliceName: string): string {
|
|
475
|
+
if (schemaDefs[sliceName]) return sliceName;
|
|
476
|
+
const alias = ALIAS_MAP[sliceName.toLowerCase()];
|
|
477
|
+
if (alias && schemaDefs[alias]) return alias;
|
|
478
|
+
// Case-insensitive fallback
|
|
479
|
+
const lower = sliceName.toLowerCase();
|
|
480
|
+
for (const key of Object.keys(schemaDefs)) {
|
|
481
|
+
if (key.toLowerCase() === lower) return key;
|
|
482
|
+
}
|
|
483
|
+
throw new DocumentsValidationError(`unknown schema slice "${sliceName}"`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function extractRefKeys(obj: unknown, refs: Set<string>): void {
|
|
487
|
+
if (typeof obj !== "object" || obj === null) return;
|
|
488
|
+
if (Array.isArray(obj)) {
|
|
489
|
+
for (const item of obj) extractRefKeys(item, refs);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const record = obj as Record<string, unknown>;
|
|
493
|
+
for (const [key, value] of Object.entries(record)) {
|
|
494
|
+
if (key === "$ref" && typeof value === "string") {
|
|
495
|
+
const match = value.match(/^#\/\$defs\/([A-Za-z0-9_]+)$/);
|
|
496
|
+
if (match) refs.add(match[1]);
|
|
497
|
+
} else {
|
|
498
|
+
extractRefKeys(value, refs);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function calculateDefsClosure(allDefs: Record<string, JsonSchema>, rootKeys: readonly string[]): Record<string, JsonSchema> {
|
|
504
|
+
const collected = new Set<string>();
|
|
505
|
+
const queue = [...rootKeys];
|
|
506
|
+
|
|
507
|
+
while (queue.length > 0) {
|
|
508
|
+
const key = queue.pop()!;
|
|
509
|
+
if (collected.has(key)) continue;
|
|
510
|
+
collected.add(key);
|
|
511
|
+
const def = allDefs[key];
|
|
512
|
+
if (!def) continue;
|
|
513
|
+
|
|
514
|
+
const childRefs = new Set<string>();
|
|
515
|
+
extractRefKeys(def, childRefs);
|
|
516
|
+
for (const child of childRefs) {
|
|
517
|
+
if (!collected.has(child) && allDefs[child]) {
|
|
518
|
+
queue.push(child);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const result: Record<string, JsonSchema> = {};
|
|
524
|
+
for (const key of collected) {
|
|
525
|
+
result[key] = allDefs[key];
|
|
526
|
+
}
|
|
527
|
+
return result;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Returns the full Draft-07 JSON Schema for the given document kind, or a
|
|
532
|
+
* minimal dependency-closure schema for the requested slice(s).
|
|
533
|
+
*/
|
|
534
|
+
export function documentModelSchema(kind: DocumentKind, slice?: string | readonly string[]): JsonSchema {
|
|
535
|
+
const baseSchema = SCHEMAS_BY_KIND[kind];
|
|
536
|
+
if (!baseSchema) {
|
|
537
|
+
throw new DocumentsValidationError(`unsupported document kind: ${String(kind)}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const defs = (baseSchema.$defs ?? {}) as Record<string, JsonSchema>;
|
|
541
|
+
|
|
542
|
+
if (!slice || (Array.isArray(slice) && slice.length === 0)) {
|
|
543
|
+
return structuredClone(baseSchema);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const sliceList = Array.isArray(slice) ? slice : [slice];
|
|
547
|
+
const rootKeys = sliceList.map((s) => resolveDefKey(defs, s));
|
|
548
|
+
const closureDefs = calculateDefsClosure(defs, rootKeys);
|
|
549
|
+
|
|
550
|
+
const slicedSchema: JsonSchema = {
|
|
551
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
552
|
+
title: `Prism Document Schema Slice (${kind}: ${rootKeys.join(", ")})`,
|
|
553
|
+
$defs: closureDefs,
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
if (rootKeys.length === 1) {
|
|
557
|
+
slicedSchema.$ref = `#/$defs/${rootKeys[0]}`;
|
|
558
|
+
} else {
|
|
559
|
+
slicedSchema.anyOf = rootKeys.map((key) => ({ $ref: `#/$defs/${key}` }));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return slicedSchema;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const CANONICAL_DECIMAL_REGEX = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
566
|
+
|
|
567
|
+
function validateInvariants(model: DocumentModel): void {
|
|
568
|
+
if (model.kind === "doc") {
|
|
569
|
+
const doc = model as DocModel;
|
|
570
|
+
for (let index = 0; index < doc.blocks.length; index += 1) {
|
|
571
|
+
const block = doc.blocks[index];
|
|
572
|
+
if (block.type === "table") {
|
|
573
|
+
const table = block as TableBlock;
|
|
574
|
+
if (table.cells.length !== table.rows) {
|
|
575
|
+
throw new DocumentsValidationError(
|
|
576
|
+
`table block at index ${index} declares ${table.rows} rows but contains ${table.cells.length} rows`,
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
for (let r = 0; r < table.cells.length; r += 1) {
|
|
580
|
+
const row = table.cells[r];
|
|
581
|
+
if (row.length !== table.columns) {
|
|
582
|
+
throw new DocumentsValidationError(
|
|
583
|
+
`table block at index ${index} row ${r} declares ${table.columns} columns but contains ${row.length} cells`,
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
} else if (model.kind === "sheet") {
|
|
590
|
+
const sheetModel = model as SheetModel;
|
|
591
|
+
for (let s = 0; s < sheetModel.sheets.length; s += 1) {
|
|
592
|
+
const sheet = sheetModel.sheets[s];
|
|
593
|
+
for (let r = 0; r < sheet.cells.length; r += 1) {
|
|
594
|
+
const row = sheet.cells[r];
|
|
595
|
+
for (let c = 0; c < row.length; c += 1) {
|
|
596
|
+
const cell = row[c];
|
|
597
|
+
if (cell !== null && typeof cell === "object" && "type" in cell && cell.type === "decimal") {
|
|
598
|
+
if (!CANONICAL_DECIMAL_REGEX.test(cell.value)) {
|
|
599
|
+
throw new DocumentsValidationError(
|
|
600
|
+
`sheet "${sheet.name}" cell at row ${r}, col ${c} has invalid decimal string "${cell.value}"`,
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Validates untrusted input against the Prism Document Model Draft-07 schema
|
|
612
|
+
* and structural invariants. Throws {@link DocumentsValidationError} on failure.
|
|
613
|
+
*/
|
|
614
|
+
export function validateDocumentModel(model: unknown): asserts model is DocumentModel {
|
|
615
|
+
if (typeof model !== "object" || model === null || Array.isArray(model)) {
|
|
616
|
+
throw new DocumentsValidationError("model must be a non-null object");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const candidate = model as Record<string, unknown>;
|
|
620
|
+
const kind = candidate.kind;
|
|
621
|
+
if (kind !== "doc" && kind !== "sheet" && kind !== "deck") {
|
|
622
|
+
throw new DocumentsValidationError(`unknown document kind: "${String(kind)}" (must be "doc", "sheet", or "deck")`);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (typeof candidate.modelVersion !== "number" || !Number.isSafeInteger(candidate.modelVersion) || candidate.modelVersion <= 0) {
|
|
626
|
+
throw new DocumentsValidationError("modelVersion must be a positive safe integer");
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const validator = getValidator(kind as DocumentKind);
|
|
630
|
+
const valid = validator(model);
|
|
631
|
+
if (!valid) {
|
|
632
|
+
const firstError = validator.errors?.[0];
|
|
633
|
+
const path = firstError?.instancePath || "root";
|
|
634
|
+
const msg = firstError?.message || "schema validation failed";
|
|
635
|
+
throw new DocumentsValidationError(`model validation failed at ${path}: ${msg}`);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
validateInvariants(model as DocumentModel);
|
|
639
|
+
}
|