@openplanr/protocol 0.2.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/LICENSE +21 -0
- package/README.md +82 -0
- package/lib/dashboard/closed-json-contract.mjs +141 -0
- package/lib/dashboard/generated/operate-experience-surface-schema-data.mjs +67 -0
- package/lib/dashboard/generated/operate-review-schema-data.mjs +35 -0
- package/lib/dashboard/generated/operate-schema-token-codec.mjs +127 -0
- package/lib/dashboard/operate-experience-audit-display-contract.d.mts +196 -0
- package/lib/dashboard/operate-experience-audit-display-contract.mjs +700 -0
- package/lib/dashboard/operate-experience-display-contract.d.mts +572 -0
- package/lib/dashboard/operate-experience-display-contract.mjs +1007 -0
- package/lib/dashboard/operate-experience-surface-contract.d.mts +157 -0
- package/lib/dashboard/operate-experience-surface-contract.mjs +91 -0
- package/lib/dashboard/operate-review-contract.d.mts +34 -0
- package/lib/dashboard/operate-review-contract.mjs +175 -0
- package/lib/dashboard/operate-review-display-workspace-contract.d.mts +54 -0
- package/lib/dashboard/operate-review-display-workspace-contract.mjs +258 -0
- package/lib/dashboard/operate-review-payload-safety.d.mts +7 -0
- package/lib/dashboard/operate-review-payload-safety.mjs +42 -0
- package/lib/dashboard/operate-review-workspace-projection-v2.d.mts +218 -0
- package/lib/protocol/index.d.ts +2638 -0
- package/package.json +115 -0
- package/registries/commands.json +1952 -0
- package/registries/diagram-grammars.json +2407 -0
- package/registries/diagram-semantic-patterns.json +111 -0
- package/registries/output-paths.json +40 -0
- package/registries/outputs.json +864 -0
- package/registries/roles.json +661 -0
- package/registries/rules.json +261 -0
- package/registries/skill-host-profiles.json +247 -0
- package/registries/skill-modules.json +1112 -0
- package/registries/skill-routing.json +389 -0
- package/registries/skills.json +1545 -0
- package/registries/task-kinds.json +220 -0
- package/registry/adapters.json +110 -0
- package/registry/artifact-theme.json +60 -0
- package/registry/evaluation-graders.json +303 -0
- package/registry/evaluation-host-profiles.json +131 -0
- package/registry/frozen-commands.json +15 -0
- package/registry/landing-operations.json +20 -0
- package/registry/landing-workflows.json +70 -0
- package/registry/live-evidence-providers.json +1 -0
- package/registry/operate-v2-contracts.json +5965 -0
- package/registry/professional-skills.json +115 -0
- package/registry/roles.json +14 -0
- package/registry/ship-review-specialists.json +42 -0
- package/schemas/v1.0.0/design-approved.schema.json +35 -0
- package/schemas/v1.0.0/design-feedback.schema.json +169 -0
- package/schemas/v1.0.0/design-manifest.schema.json +90 -0
- package/schemas/v1.0.0/design-session.schema.json +73 -0
- package/schemas/v1.0.0/graph.schema.json +100 -0
- package/schemas/v1.0.0/pipeline-shipped.schema.json +154 -0
- package/schemas/v1.0.0/run-manifest.schema.json +81 -0
- package/schemas/v1.0.0/spec.schema.json +120 -0
- package/schemas/v1.0.0/stack.schema.json +184 -0
- package/schemas/v1.0.0/story.schema.json +95 -0
- package/schemas/v1.0.0/task.schema.json +171 -0
- package/schemas/v1.0.0/taste-profile.schema.json +96 -0
- package/schemas/v1.1.0/adapter-registry.schema.json +72 -0
- package/schemas/v1.1.0/artifact-envelope.schema.json +142 -0
- package/schemas/v1.1.0/artifact-paste.schema.json +49 -0
- package/schemas/v1.1.0/artifact-review.schema.json +97 -0
- package/schemas/v1.1.0/artifact-room-descriptor.schema.json +39 -0
- package/schemas/v1.1.0/artifact-room-event.schema.json +184 -0
- package/schemas/v1.1.0/artifact-room-signed-event.schema.json +58 -0
- package/schemas/v1.1.0/artifact-theme.schema.json +139 -0
- package/schemas/v1.1.0/browser-qa-gate.schema.json +21 -0
- package/schemas/v1.1.0/browser-qa-result.schema.json +24 -0
- package/schemas/v1.1.0/browser-qa-session.schema.json +12 -0
- package/schemas/v1.1.0/ecosystem-manifest.schema.json +46 -0
- package/schemas/v1.1.0/investigation-diagnosis.schema.json +12 -0
- package/schemas/v1.1.0/investigation-event.schema.json +15 -0
- package/schemas/v1.1.0/investigation-experiment.schema.json +17 -0
- package/schemas/v1.1.0/investigation-fix.schema.json +11 -0
- package/schemas/v1.1.0/investigation-hypothesis.schema.json +6 -0
- package/schemas/v1.1.0/investigation-observation.schema.json +10 -0
- package/schemas/v1.1.0/investigation-receipt.schema.json +7 -0
- package/schemas/v1.1.0/investigation-record.schema.json +7 -0
- package/schemas/v1.1.0/investigation-request.schema.json +34 -0
- package/schemas/v1.1.0/planning-review-event.schema.json +64 -0
- package/schemas/v1.1.0/planning-review-receipt.schema.json +19 -0
- package/schemas/v1.1.0/planning-review-record.schema.json +176 -0
- package/schemas/v1.1.0/planning-review-state.schema.json +19 -0
- package/schemas/v1.1.0/professional-skills.schema.json +105 -0
- package/schemas/v1.1.0/professional-specification.schema.json +111 -0
- package/schemas/v1.1.0/provenance-event.schema.json +82 -0
- package/schemas/v1.1.0/role-registry.schema.json +28 -0
- package/schemas/v1.1.0/runtime-lock.schema.json +39 -0
- package/schemas/v1.1.0/ship-closure.schema.json +353 -0
- package/schemas/v1.1.0/ship-review-specialist-registry.schema.json +29 -0
- package/schemas/v1.1.0/ship-risk-classification.schema.json +48 -0
- package/schemas/v1.1.0/specialist-review-result.schema.json +19 -0
- package/schemas/v1.10.0/design-review-bundle.schema.json +584 -0
- package/schemas/v1.10.0/design-review-context.schema.json +191 -0
- package/schemas/v1.10.0/design-review-handoff.schema.json +407 -0
- package/schemas/v1.10.0/design-review-metadata-payload.schema.json +106 -0
- package/schemas/v1.11.0/design-review-metadata-payload.schema.json +107 -0
- package/schemas/v1.12.0/enterprise-access-context.schema.json +217 -0
- package/schemas/v1.12.0/enterprise-agent-handoff.schema.json +508 -0
- package/schemas/v1.12.0/enterprise-artifact-revision.schema.json +87 -0
- package/schemas/v1.12.0/enterprise-change-proposal.schema.json +359 -0
- package/schemas/v1.12.0/enterprise-evidence-reference.schema.json +147 -0
- package/schemas/v1.12.0/enterprise-review-thread.schema.json +278 -0
- package/schemas/v1.12.0/enterprise-sync-state.schema.json +169 -0
- package/schemas/v1.2.0/adapter-registry.schema.json +83 -0
- package/schemas/v1.2.0/dashboard-bootstrap.schema.json +219 -0
- package/schemas/v1.2.0/ecosystem-release-operation.schema.json +90 -0
- package/schemas/v1.2.0/ecosystem-saga.schema.json +54 -0
- package/schemas/v1.2.0/evidence-diagnostic.schema.json +77 -0
- package/schemas/v1.2.0/guided-answer-envelope.schema.json +73 -0
- package/schemas/v1.2.0/guided-confirmation.schema.json +128 -0
- package/schemas/v1.2.0/guided-question.schema.json +242 -0
- package/schemas/v1.2.0/guided-questionnaire.schema.json +278 -0
- package/schemas/v1.2.0/guided-session.schema.json +141 -0
- package/schemas/v1.2.0/landing-confirmation.schema.json +17 -0
- package/schemas/v1.2.0/landing-event.schema.json +12 -0
- package/schemas/v1.2.0/landing-operation-registry.schema.json +16 -0
- package/schemas/v1.2.0/landing-phase-receipt.schema.json +19 -0
- package/schemas/v1.2.0/landing-plan.schema.json +36 -0
- package/schemas/v1.2.0/landing-receipt.schema.json +12 -0
- package/schemas/v1.2.0/landing-workflow-catalog.schema.json +143 -0
- package/schemas/v1.2.0/operate-action-display-workspace.schema.json +119 -0
- package/schemas/v1.2.0/operate-cycle-display-workspace.schema.json +109 -0
- package/schemas/v1.2.0/operate-executive-board-display-surface.schema.json +72 -0
- package/schemas/v1.2.0/operate-experience-audit-display-surface.schema.json +328 -0
- package/schemas/v1.2.0/operate-experience-display-surface.schema.json +46 -0
- package/schemas/v1.2.0/operate-experience-surface.schema.json +590 -0
- package/schemas/v1.2.0/operate-recovery-display-surface.schema.json +85 -0
- package/schemas/v1.2.0/structured-action.schema.json +91 -0
- package/schemas/v1.3.0/adapter-registry.schema.json +84 -0
- package/schemas/v1.3.0/ecosystem-manifest.schema.json +120 -0
- package/schemas/v1.3.0/release-compatibility-claim.schema.json +46 -0
- package/schemas/v1.3.0/release-ledger-receipt.schema.json +45 -0
- package/schemas/v1.3.0/release-ledger.schema.json +60 -0
- package/schemas/v1.4.0/adapter-registry.schema.json +62 -0
- package/schemas/v1.4.0/evaluation-aggregate-report.schema.json +166 -0
- package/schemas/v1.4.0/evaluation-budget.schema.json +49 -0
- package/schemas/v1.4.0/evaluation-corpus.schema.json +93 -0
- package/schemas/v1.4.0/evaluation-fixture.schema.json +64 -0
- package/schemas/v1.4.0/evaluation-gate-policy.schema.json +33 -0
- package/schemas/v1.4.0/evaluation-grader-registration.schema.json +141 -0
- package/schemas/v1.4.0/evaluation-grader-registry.schema.json +20 -0
- package/schemas/v1.4.0/evaluation-host-profile-registry.schema.json +20 -0
- package/schemas/v1.4.0/evaluation-host-profile.schema.json +73 -0
- package/schemas/v1.4.0/evaluation-observation.schema.json +76 -0
- package/schemas/v1.4.0/evaluation-run-result.schema.json +84 -0
- package/schemas/v1.4.0/evaluation-scenario.schema.json +231 -0
- package/schemas/v1.4.0/evaluation-waiver.schema.json +65 -0
- package/schemas/v1.4.0/skill-certification-receipt.schema.json +103 -0
- package/schemas/v1.5.0/command-catalog.schema.json +296 -0
- package/schemas/v1.5.0/common.schema.json +261 -0
- package/schemas/v1.5.0/generated-asset-manifest.schema.json +313 -0
- package/schemas/v1.5.0/implementation-result.schema.json +152 -0
- package/schemas/v1.5.0/migration-preservation-manifest.schema.json +749 -0
- package/schemas/v1.5.0/output-catalog.schema.json +210 -0
- package/schemas/v1.5.0/output-path-catalog.schema.json +81 -0
- package/schemas/v1.5.0/role-registry.schema.json +261 -0
- package/schemas/v1.5.0/rule-catalog.schema.json +130 -0
- package/schemas/v1.5.0/skill-catalog.schema.json +303 -0
- package/schemas/v1.5.0/task-kind-registry.schema.json +167 -0
- package/schemas/v1.5.0/task-manifest.schema.json +373 -0
- package/schemas/v1.5.0/task-output-manifest.schema.json +462 -0
- package/schemas/v1.6.0/common.schema.json +462 -0
- package/schemas/v1.6.0/diagram-consumer-reference.schema.json +167 -0
- package/schemas/v1.6.0/diagram-document.schema.json +699 -0
- package/schemas/v1.6.0/diagram-fidelity-report.schema.json +137 -0
- package/schemas/v1.6.0/diagram-manifest.schema.json +162 -0
- package/schemas/v1.6.0/diagram-quality-report.schema.json +148 -0
- package/schemas/v1.6.0/diagram-review-binding.schema.json +132 -0
- package/schemas/v1.6.0/diagram-semantic-pattern-registry.schema.json +90 -0
- package/schemas/v1.6.0/diagram-type-registry.schema.json +373 -0
- package/schemas/v1.6.0/generated-asset-manifest.schema.json +103 -0
- package/schemas/v1.6.0/skill-catalog.schema.json +84 -0
- package/schemas/v1.6.0/skill-completion-receipt.schema.json +72 -0
- package/schemas/v1.6.0/skill-consent-record.schema.json +74 -0
- package/schemas/v1.6.0/skill-host-profile-registry.schema.json +526 -0
- package/schemas/v1.6.0/skill-learning-record.schema.json +116 -0
- package/schemas/v1.6.0/skill-module-registry.schema.json +108 -0
- package/schemas/v1.6.0/skill-routing-registry.schema.json +93 -0
- package/schemas/v1.6.0/skill-session.schema.json +73 -0
- package/schemas/v1.6.0/skill-source.schema.json +169 -0
- package/schemas/v1.7.0/common.schema.json +464 -0
- package/schemas/v1.7.0/generated-asset-manifest.schema.json +103 -0
- package/schemas/v1.7.0/planning-id-sequence.schema.json +50 -0
- package/schemas/v1.7.0/request-authority.schema.json +71 -0
- package/schemas/v1.7.0/skill-catalog.schema.json +84 -0
- package/schemas/v1.7.0/skill-completion-receipt.schema.json +72 -0
- package/schemas/v1.7.0/skill-consent-record.schema.json +74 -0
- package/schemas/v1.7.0/skill-host-profile-registry.schema.json +526 -0
- package/schemas/v1.7.0/skill-learning-record.schema.json +116 -0
- package/schemas/v1.7.0/skill-module-registry.schema.json +108 -0
- package/schemas/v1.7.0/skill-routing-registry.schema.json +93 -0
- package/schemas/v1.7.0/skill-session.schema.json +73 -0
- package/schemas/v1.7.0/skill-source.schema.json +169 -0
- package/schemas/v1.7.0/spec.schema.json +117 -0
- package/schemas/v1.7.0/story.schema.json +107 -0
- package/schemas/v1.7.0/task.schema.json +217 -0
- package/schemas/v1.8.0/common.schema.json +96 -0
- package/schemas/v1.8.0/skill-package.schema.json +63 -0
- package/schemas/v1.8.0/utility-command-catalog.schema.json +82 -0
- package/schemas/v1.9.0/design-document.schema.json +306 -0
- package/schemas/v1.9.0/design-review-bundle.schema.json +346 -0
- package/schemas/v1.9.0/design-review-workspace.schema.json +108 -0
- package/schemas/v1.9.0/design-workspace-create.schema.json +170 -0
- package/schemas/v1.9.0/design-workspace-event.schema.json +97 -0
- package/schemas/v1.9.0/design-workspace-revision.schema.json +56 -0
- package/schemas/v2.0.0/agent-runtime-manifest.schema.json +59 -0
- package/schemas/v2.0.0/business-operating-snapshot-projection.schema.json +14 -0
- package/schemas/v2.0.0/operate-allowed-action.schema.json +181 -0
- package/schemas/v2.0.0/operate-api-envelope.schema.json +225 -0
- package/schemas/v2.0.0/operate-capability-provider-registration.schema.json +30 -0
- package/schemas/v2.0.0/operate-domain-registration.schema.json +284 -0
- package/schemas/v2.0.0/operate-evidence-provider-registration.schema.json +55 -0
- package/schemas/v2.0.0/operate-evidence-resolver-registration.schema.json +46 -0
- package/schemas/v2.0.0/operate-executor-registration.schema.json +27 -0
- package/schemas/v2.0.0/operate-experience-live-patch.schema.json +47 -0
- package/schemas/v2.0.0/operate-experience-preview.schema.json +51 -0
- package/schemas/v2.0.0/operate-experience-view.schema.json +281 -0
- package/schemas/v2.0.0/operate-live-evidence-provider-registration.schema.json +48 -0
- package/schemas/v2.0.0/operate-live-evidence-provider-registry.schema.json +17 -0
- package/schemas/v2.0.0/operate-metric-provider-registration.schema.json +12 -0
- package/schemas/v2.0.0/operate-policy-provider-registration.schema.json +33 -0
- package/schemas/v2.0.0/operate-review-bound-submission.schema.json +29 -0
- package/schemas/v2.0.0/operate-review-display-workspace.schema.json +390 -0
- package/schemas/v2.0.0/operate-snapshot-provider-registration.schema.json +16 -0
- package/schemas/v2.0.0/operate-tool-call.schema.json +172 -0
- package/schemas/v2.0.0/operate-verification-provider-registration.schema.json +12 -0
- package/schemas/v2.0.0/operating-action-policy.schema.json +31 -0
- package/schemas/v2.0.0/operating-action-verification-plan.schema.json +13 -0
- package/schemas/v2.0.0/operating-action.schema.json +86 -0
- package/schemas/v2.0.0/operating-advisor-result.schema.json +394 -0
- package/schemas/v2.0.0/operating-approval-record.schema.json +23 -0
- package/schemas/v2.0.0/operating-approval-requirement.schema.json +31 -0
- package/schemas/v2.0.0/operating-artifact.schema.json +52 -0
- package/schemas/v2.0.0/operating-assignment.schema.json +437 -0
- package/schemas/v2.0.0/operating-assumption.schema.json +16 -0
- package/schemas/v2.0.0/operating-capability-availability.schema.json +15 -0
- package/schemas/v2.0.0/operating-capability-grant.schema.json +14 -0
- package/schemas/v2.0.0/operating-challenger-review.schema.json +204 -0
- package/schemas/v2.0.0/operating-checkpoint.schema.json +43 -0
- package/schemas/v2.0.0/operating-claim.schema.json +17 -0
- package/schemas/v2.0.0/operating-connector-checkpoint.schema.json +28 -0
- package/schemas/v2.0.0/operating-context-capture.schema.json +74 -0
- package/schemas/v2.0.0/operating-cycle-input-binding.schema.json +30 -0
- package/schemas/v2.0.0/operating-cycle.schema.json +42 -0
- package/schemas/v2.0.0/operating-decision-ledger.schema.json +237 -0
- package/schemas/v2.0.0/operating-decision.schema.json +137 -0
- package/schemas/v2.0.0/operating-delivery-evidence.schema.json +46 -0
- package/schemas/v2.0.0/operating-delivery-route.schema.json +43 -0
- package/schemas/v2.0.0/operating-delta.schema.json +13 -0
- package/schemas/v2.0.0/operating-domain-projection.schema.json +15 -0
- package/schemas/v2.0.0/operating-event-trigger.schema.json +11 -0
- package/schemas/v2.0.0/operating-event.schema.json +505 -0
- package/schemas/v2.0.0/operating-evidence-candidate.schema.json +88 -0
- package/schemas/v2.0.0/operating-evidence-edge.schema.json +39 -0
- package/schemas/v2.0.0/operating-evidence-graph.schema.json +25 -0
- package/schemas/v2.0.0/operating-evidence-observation.schema.json +48 -0
- package/schemas/v2.0.0/operating-evidence-ref.schema.json +51 -0
- package/schemas/v2.0.0/operating-evidence-resolution.schema.json +54 -0
- package/schemas/v2.0.0/operating-execution-result.schema.json +23 -0
- package/schemas/v2.0.0/operating-executive-board.schema.json +78 -0
- package/schemas/v2.0.0/operating-finding.schema.json +116 -0
- package/schemas/v2.0.0/operating-governed-operation.schema.json +54 -0
- package/schemas/v2.0.0/operating-intelligence-input-bundle.schema.json +128 -0
- package/schemas/v2.0.0/operating-intelligence-plan.schema.json +302 -0
- package/schemas/v2.0.0/operating-learning-receipt.schema.json +31 -0
- package/schemas/v2.0.0/operating-learning.schema.json +13 -0
- package/schemas/v2.0.0/operating-live-evidence-consent-record.schema.json +42 -0
- package/schemas/v2.0.0/operating-live-evidence-ingestion.schema.json +31 -0
- package/schemas/v2.0.0/operating-measurement-plan.schema.json +18 -0
- package/schemas/v2.0.0/operating-measurement-schedule-receipt.schema.json +7 -0
- package/schemas/v2.0.0/operating-measurement-schedule.schema.json +16 -0
- package/schemas/v2.0.0/operating-metric-observation.schema.json +15 -0
- package/schemas/v2.0.0/operating-metric.schema.json +18 -0
- package/schemas/v2.0.0/operating-model-state.schema.json +36 -0
- package/schemas/v2.0.0/operating-objective.schema.json +21 -0
- package/schemas/v2.0.0/operating-origin.schema.json +31 -0
- package/schemas/v2.0.0/operating-outcome-evaluation.schema.json +46 -0
- package/schemas/v2.0.0/operating-outcome.schema.json +13 -0
- package/schemas/v2.0.0/operating-planning-proposal.schema.json +45 -0
- package/schemas/v2.0.0/operating-policy-evaluation.schema.json +28 -0
- package/schemas/v2.0.0/operating-review-read.schema.json +217 -0
- package/schemas/v2.0.0/operating-review-receipt.schema.json +64 -0
- package/schemas/v2.0.0/operating-review.schema.json +47 -0
- package/schemas/v2.0.0/operating-risk.schema.json +96 -0
- package/schemas/v2.0.0/operating-rollback-plan.schema.json +10 -0
- package/schemas/v2.0.0/operating-rollback-result.schema.json +20 -0
- package/schemas/v2.0.0/operating-runtime-state.schema.json +289 -0
- package/schemas/v2.0.0/operating-scenario.schema.json +13 -0
- package/schemas/v2.0.0/operating-snapshot.schema.json +38 -0
- package/schemas/v2.0.0/operating-submission.schema.json +34 -0
- package/schemas/v2.0.0/operating-trace-matrix.schema.json +176 -0
- package/schemas/v2.0.0/operating-work-change-set.schema.json +89 -0
- package/schemas/v2.0.0/operating-work-ledger.schema.json +40 -0
- package/schemas/v2.0.0/software-operating-snapshot-projection.schema.json +14 -0
- package/src/browser-contracts.d.mts +5 -0
- package/src/browser-contracts.mjs +40 -0
- package/src/canonical-json.d.mts +5 -0
- package/src/canonical-json.mjs +142 -0
- package/src/compat/live-evidence-v2.mjs +1019 -0
- package/src/compat/operate-experience-live-patch.mjs +66 -0
- package/src/compat/operating-planning-contracts.mjs +114 -0
- package/src/contracts.d.mts +42 -0
- package/src/contracts.mjs +990 -0
- package/src/design-contracts.d.mts +62 -0
- package/src/design-contracts.mjs +186 -0
- package/src/design-publication-contracts.d.mts +4 -0
- package/src/design-publication-contracts.mjs +85 -0
- package/src/diagram-contracts.d.mts +19 -0
- package/src/diagram-contracts.mjs +28 -0
- package/src/enterprise-contracts.d.mts +107 -0
- package/src/enterprise-contracts.mjs +463 -0
- package/src/errors.d.mts +11 -0
- package/src/errors.mjs +109 -0
- package/src/generated/canonical-registries.mjs +5554 -0
- package/src/generated/contract-catalog-v2.mjs +7076 -0
- package/src/generated/diagram-registries.mjs +2529 -0
- package/src/index.d.mts +10 -0
- package/src/index.mjs +40 -0
- package/src/json-schema.d.mts +7 -0
- package/src/json-schema.mjs +283 -0
- package/src/planning-contracts.d.mts +10 -0
- package/src/planning-contracts.mjs +53 -0
- package/src/registries.d.mts +40 -0
- package/src/registries.mjs +261 -0
- package/src/review-experience-contracts.d.mts +11 -0
- package/src/review-experience-contracts.mjs +103 -0
- package/src/semver.d.mts +2 -0
- package/src/semver.mjs +7 -0
- package/src/skill-source-contracts.d.mts +25 -0
- package/src/skill-source-contracts.mjs +105 -0
- package/src/task-contracts.d.mts +7 -0
- package/src/task-contracts.mjs +130 -0
- package/src/workspace-contracts.d.mts +26 -0
- package/src/workspace-contracts.mjs +83 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { canonicalizeJson, sha256Hex } from './canonical-json.mjs';
|
|
2
|
+
import { validateJson } from './json-schema.mjs';
|
|
3
|
+
|
|
4
|
+
export const ENTERPRISE_CONTRACT_VERSION = '1.0.0';
|
|
5
|
+
export const ENTERPRISE_PROTOCOL_VERSION = '1.12.0';
|
|
6
|
+
export const ENTERPRISE_CONTENT_DIGEST_HEADER = 'X-OpenPlanr-Content-Digest';
|
|
7
|
+
export const ENTERPRISE_ID_PATTERN = '^[A-Za-z0-9][A-Za-z0-9_-]{0,127}(?![\\s\\S])';
|
|
8
|
+
export const ENTERPRISE_REVIEW_CATEGORIES = Object.freeze(['question', 'suggestion', 'change-request', 'blocker']);
|
|
9
|
+
export const ENTERPRISE_ACTIONS = Object.freeze([
|
|
10
|
+
'organization.manage', 'ownership.transfer', 'billing.manage', 'audit.read', 'project.create', 'project.manage',
|
|
11
|
+
'artifact.read', 'artifact.author', 'artifact.export', 'review.write', 'review.resolve',
|
|
12
|
+
]);
|
|
13
|
+
const id = { type: 'string', pattern: ENTERPRISE_ID_PATTERN };
|
|
14
|
+
const semanticId = { type: 'string', minLength: 1, maxLength: 512, pattern: '^[^\\s\\u0000-\\u001f\\u007f]+$' };
|
|
15
|
+
const nullableId = { anyOf: [id, { type: 'null' }] };
|
|
16
|
+
const digest = { type: 'string', minLength: 64, maxLength: 64, pattern: '^[a-f0-9]{64}$' };
|
|
17
|
+
const timestamp = { type: 'string', format: 'date-time', pattern: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$' };
|
|
18
|
+
const text = { type: 'string', minLength: 1, maxLength: 16384 };
|
|
19
|
+
const title = { ...text, maxLength: 240 };
|
|
20
|
+
const closed = (properties, required = Object.keys(properties)) => ({ type: 'object', additionalProperties: false, properties, required });
|
|
21
|
+
const list = (items, maxItems = 1000) => ({ type: 'array', items, maxItems });
|
|
22
|
+
const schema = (name, properties, required) => ({
|
|
23
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
24
|
+
$id: `https://openplanr.dev/schemas/v1.12.0/${name}.schema.json`,
|
|
25
|
+
'x-openplanr-contract': { id: name, version: ENTERPRISE_PROTOCOL_VERSION },
|
|
26
|
+
...closed(properties, required),
|
|
27
|
+
});
|
|
28
|
+
const envelope = (kind) => ({ kind: { const: `openplanr-${kind}` }, schemaVersion: { const: ENTERPRISE_CONTRACT_VERSION } });
|
|
29
|
+
const scoped = { organizationId: id, projectId: id };
|
|
30
|
+
const versionedArtifact = { ...scoped, artifactId: id, revisionId: id };
|
|
31
|
+
const activeStatus = { enum: ['active', 'revoked'] };
|
|
32
|
+
const projectRole = { enum: ['maintainer', 'author', 'reviewer', 'viewer'] };
|
|
33
|
+
|
|
34
|
+
export const ENTERPRISE_ACCESS_CONTEXT_SCHEMA = schema('enterprise-access-context', {
|
|
35
|
+
actor: closed({ id, verified: { type: 'boolean' } }),
|
|
36
|
+
organization: closed({ id, status: { enum: ['active', 'suspended'] } }),
|
|
37
|
+
membership: closed({ organizationId: id, actorId: id, role: { enum: ['owner', 'admin', 'member'] }, status: activeStatus }),
|
|
38
|
+
projectMembership: closed({ ...scoped, actorId: id, role: projectRole, status: activeStatus }),
|
|
39
|
+
guestGrant: closed({ ...scoped, artifactId: id, actorId: id, role: { enum: ['reviewer', 'viewer'] }, status: activeStatus, expiresAt: timestamp }),
|
|
40
|
+
resource: closed({ organizationId: id, projectId: id, artifactId: id }, ['organizationId']),
|
|
41
|
+
action: { enum: ENTERPRISE_ACTIONS }, now: timestamp,
|
|
42
|
+
}, ['actor', 'organization', 'resource', 'action', 'now']);
|
|
43
|
+
|
|
44
|
+
export const ENTERPRISE_ARTIFACT_REVISION_SCHEMA = schema('enterprise-artifact-revision', {
|
|
45
|
+
...envelope('enterprise-artifact-revision'), id, ...scoped, artifactId: id, parentRevisionId: nullableId,
|
|
46
|
+
contentDigest: digest, contentType: { enum: ['application/json', 'text/markdown', 'image/svg+xml', 'text/html'] },
|
|
47
|
+
byteLength: { type: 'integer', minimum: 0, maximum: 5 * 1024 * 1024 }, createdAt: timestamp, actorId: id,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const ENTERPRISE_REVIEW_ANCHOR_SCHEMA = closed({
|
|
51
|
+
revisionId: id, elementId: semanticId, screenId: semanticId, frameId: semanticId,
|
|
52
|
+
x: { type: 'number', minimum: 0, maximum: 1 }, y: { type: 'number', minimum: 0, maximum: 1 },
|
|
53
|
+
}, ['revisionId']);
|
|
54
|
+
ENTERPRISE_REVIEW_ANCHOR_SCHEMA.anyOf = [{ required: ['elementId'] }, { required: ['screenId', 'frameId'] }, { required: ['x', 'y'] }];
|
|
55
|
+
ENTERPRISE_REVIEW_ANCHOR_SCHEMA.allOf = [
|
|
56
|
+
{ if: { required: ['x'] }, then: { required: ['y'] } },
|
|
57
|
+
{ if: { required: ['y'] }, then: { required: ['x'] } },
|
|
58
|
+
{ if: { required: ['frameId'] }, then: { required: ['screenId'] } },
|
|
59
|
+
];
|
|
60
|
+
const reply = closed({ id, authorId: id, body: text, createdAt: timestamp });
|
|
61
|
+
export const ENTERPRISE_REVIEW_THREAD_SCHEMA = schema('enterprise-review-thread', {
|
|
62
|
+
...envelope('enterprise-review-thread'), id, ...scoped, artifactId: id, anchor: ENTERPRISE_REVIEW_ANCHOR_SCHEMA,
|
|
63
|
+
category: { enum: ENTERPRISE_REVIEW_CATEGORIES }, status: { enum: ['open', 'addressed', 'resolved'] }, authorId: id,
|
|
64
|
+
body: text, createdAt: timestamp, updatedAt: timestamp, replies: list(reply, 10000),
|
|
65
|
+
assigneeId: nullableId, addressedRevisionId: id, resolvedAt: timestamp,
|
|
66
|
+
}, ['kind', 'schemaVersion', 'id', 'organizationId', 'projectId', 'artifactId', 'anchor', 'category', 'status', 'authorId', 'body', 'createdAt', 'updatedAt', 'replies']);
|
|
67
|
+
ENTERPRISE_REVIEW_THREAD_SCHEMA.allOf = [
|
|
68
|
+
{ if: { properties: { status: { const: 'addressed' } } }, then: { required: ['addressedRevisionId'], not: { required: ['resolvedAt'] } } },
|
|
69
|
+
{ if: { properties: { status: { const: 'resolved' } } }, then: { required: ['resolvedAt'] }, else: { not: { required: ['resolvedAt'] } } },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const collection = { enum: ['nodes', 'relations', 'groups', 'events', 'items'] };
|
|
73
|
+
export const ENTERPRISE_CHANGE_OPERATION_SCHEMA = { oneOf: [
|
|
74
|
+
closed({ op: { const: 'replace-document' }, content: { type: 'object' } }),
|
|
75
|
+
closed({ op: { const: 'set-field' }, targetId: semanticId, field: { enum: ['label', 'description', 'status', 'title'] }, value: { type: 'string', maxLength: 16384 } }),
|
|
76
|
+
closed({ op: { const: 'add-element' }, collection, element: { type: 'object', required: ['id'], properties: { id: semanticId }, additionalProperties: true } }),
|
|
77
|
+
closed({ op: { const: 'remove-element' }, collection, targetId: semanticId }),
|
|
78
|
+
closed({ op: { const: 'set-layout' }, targetId: semanticId, x: { type: 'number', minimum: -1e7, maximum: 1e7 }, y: { type: 'number', minimum: -1e7, maximum: 1e7 } }),
|
|
79
|
+
] };
|
|
80
|
+
export const ENTERPRISE_CHANGE_PROPOSAL_SCHEMA = schema('enterprise-change-proposal', {
|
|
81
|
+
...envelope('enterprise-change-proposal'), id, ...scoped, artifactId: id, baseRevisionId: id,
|
|
82
|
+
authorId: id, createdAt: timestamp, status: { enum: ['draft', 'proposed', 'accepted', 'rejected', 'applied', 'conflicted'] },
|
|
83
|
+
summary: title, operations: { ...list(ENTERPRISE_CHANGE_OPERATION_SCHEMA, 1000), minItems: 1 },
|
|
84
|
+
validation: closed({ status: { enum: ['pending', 'passed', 'failed'] }, issues: list(closed({ code: id, message: text, targetId: semanticId }, ['code', 'message'])) }),
|
|
85
|
+
application: closed({ revisionId: id, appliedAt: timestamp, actorId: id, gitCommit: { type: 'string', pattern: '^(?:[a-f0-9]{40}|[a-f0-9]{64})$' } }, ['revisionId', 'appliedAt', 'actorId']),
|
|
86
|
+
}, ['kind', 'schemaVersion', 'id', 'organizationId', 'projectId', 'artifactId', 'baseRevisionId', 'authorId', 'createdAt', 'status', 'summary', 'operations', 'validation']);
|
|
87
|
+
ENTERPRISE_CHANGE_PROPOSAL_SCHEMA.allOf = [
|
|
88
|
+
{ if: { properties: { status: { const: 'applied' } } }, then: { required: ['application'], properties: { validation: { properties: { status: { const: 'passed' } } } } }, else: { not: { required: ['application'] } } },
|
|
89
|
+
{ if: { properties: { status: { const: 'accepted' } } }, then: { properties: { validation: { properties: { status: { const: 'passed' } } } } } },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
export const ENTERPRISE_EVIDENCE_REFERENCE_SCHEMA = schema('enterprise-evidence-reference', {
|
|
93
|
+
...envelope('enterprise-evidence-reference'), id, ...scoped,
|
|
94
|
+
source: { oneOf: [
|
|
95
|
+
closed({ kind: { const: 'repository' }, repositoryId: id, path: text, commit: { type: 'string', pattern: '^(?:[a-f0-9]{40}|[a-f0-9]{64})$' }, line: { type: 'integer', minimum: 1, maximum: 1e9 } }, ['kind', 'repositoryId', 'path', 'commit']),
|
|
96
|
+
closed({ kind: { const: 'artifact' }, artifactId: id, revisionId: id, elementId: semanticId }, ['kind', 'artifactId', 'revisionId']),
|
|
97
|
+
closed({ kind: { const: 'url' }, url: { type: 'string', minLength: 1, maxLength: 2048 } }),
|
|
98
|
+
] },
|
|
99
|
+
capturedAt: timestamp, freshness: { enum: ['current', 'stale', 'unknown'] }, label: title, contentDigest: digest,
|
|
100
|
+
}, ['kind', 'schemaVersion', 'id', 'organizationId', 'projectId', 'source', 'capturedAt', 'freshness', 'label']);
|
|
101
|
+
|
|
102
|
+
export const ENTERPRISE_SYNC_STATE_SCHEMA = schema('enterprise-sync-state', {
|
|
103
|
+
...envelope('enterprise-sync-state'), ...scoped, repositoryId: id, direction: { enum: ['push', 'pull'] },
|
|
104
|
+
status: { enum: ['preview', 'pending', 'synchronized', 'failed', 'conflicted'] },
|
|
105
|
+
scope: { ...list(id, 10000), uniqueItems: true },
|
|
106
|
+
cursor: { type: ['string', 'null'], maxLength: 2048 }, operationId: id, updatedAt: timestamp,
|
|
107
|
+
items: list(closed({ artifactId: id, baseRevisionId: nullableId, revisionId: nullableId, contentDigest: digest, action: { enum: ['create', 'update', 'unchanged', 'conflict'] } }), 10000),
|
|
108
|
+
issues: list(closed({ code: id, artifactId: id, message: text }, ['code', 'message'])),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const ENTERPRISE_AGENT_HANDOFF_SCHEMA = schema('enterprise-agent-handoff', {
|
|
112
|
+
...envelope('enterprise-agent-handoff'), ...versionedArtifact, generatedAt: timestamp,
|
|
113
|
+
authority: { const: 'feedback-only' }, contentTrust: { const: 'untrusted' },
|
|
114
|
+
threads: list(ENTERPRISE_REVIEW_THREAD_SCHEMA, 10000), evidence: list(ENTERPRISE_EVIDENCE_REFERENCE_SCHEMA, 10000),
|
|
115
|
+
unresolvedUncertainties: list(text), contentDigest: digest,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const ENTERPRISE_SCHEMAS = deepFreeze({
|
|
119
|
+
'enterprise-access-context': ENTERPRISE_ACCESS_CONTEXT_SCHEMA,
|
|
120
|
+
'enterprise-artifact-revision': ENTERPRISE_ARTIFACT_REVISION_SCHEMA,
|
|
121
|
+
'enterprise-review-thread': ENTERPRISE_REVIEW_THREAD_SCHEMA,
|
|
122
|
+
'enterprise-change-proposal': ENTERPRISE_CHANGE_PROPOSAL_SCHEMA,
|
|
123
|
+
'enterprise-evidence-reference': ENTERPRISE_EVIDENCE_REFERENCE_SCHEMA,
|
|
124
|
+
'enterprise-sync-state': ENTERPRISE_SYNC_STATE_SCHEMA,
|
|
125
|
+
'enterprise-agent-handoff': ENTERPRISE_AGENT_HANDOFF_SCHEMA,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
function deepFreeze(value) {
|
|
129
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
130
|
+
for (const nested of Object.values(value)) deepFreeze(nested);
|
|
131
|
+
Object.freeze(value);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const unsafeKeys = new Set(['__proto__', 'prototype', 'constructor']);
|
|
137
|
+
function assertPlainData(value, depth = 0, seen = new Set()) {
|
|
138
|
+
if (depth > 64) throw new TypeError('Enterprise data exceeds the maximum nesting depth.');
|
|
139
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') return;
|
|
140
|
+
if (typeof value === 'number' && Number.isFinite(value)) return;
|
|
141
|
+
if (typeof value !== 'object' || seen.has(value)) throw new TypeError('Enterprise data must be finite, acyclic JSON.');
|
|
142
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError('Enterprise data must contain only plain JSON objects.');
|
|
143
|
+
seen.add(value);
|
|
144
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
145
|
+
if (unsafeKeys.has(key) || !Object.hasOwn(descriptor, 'value')) throw new TypeError('Enterprise data contains a forbidden property.');
|
|
146
|
+
assertPlainData(descriptor.value, depth + 1, seen);
|
|
147
|
+
}
|
|
148
|
+
seen.delete(value);
|
|
149
|
+
}
|
|
150
|
+
function validTimestamp(value) {
|
|
151
|
+
if (typeof value !== 'string' || !new RegExp(timestamp.pattern).test(value)) return false;
|
|
152
|
+
const parsed = new Date(value);
|
|
153
|
+
return Number.isFinite(parsed.getTime()) && parsed.toISOString() === (value.includes('.') ? value : value.replace('Z', '.000Z'));
|
|
154
|
+
}
|
|
155
|
+
function assertTimes(value, contract) {
|
|
156
|
+
if (contract.format === 'date-time' && !validTimestamp(value)) throw new TypeError('Invalid enterprise timestamp.');
|
|
157
|
+
if (Array.isArray(value) && contract.items) value.forEach(item => assertTimes(item, contract.items));
|
|
158
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
159
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
160
|
+
if (contract.properties?.[key]) assertTimes(nested, contract.properties[key]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const key of ['oneOf', 'anyOf', 'allOf']) {
|
|
164
|
+
for (const branch of contract[key] ?? []) if (!validateJson(value, branch).length) assertTimes(value, branch);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function distinct(items, key, label) {
|
|
168
|
+
const values = items.map(item => item[key]);
|
|
169
|
+
if (new Set(values).size !== values.length) throw new TypeError(`Duplicate ${label}.`);
|
|
170
|
+
}
|
|
171
|
+
function sameScope(value, expected) {
|
|
172
|
+
return value.organizationId === expected.organizationId && value.projectId === expected.projectId;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Structural validation is not authentication, signature verification, or domain validation. */
|
|
176
|
+
export function assertEnterpriseContract(value, schemaOrName) {
|
|
177
|
+
const contract = typeof schemaOrName === 'string' ? ENTERPRISE_SCHEMAS[schemaOrName] : schemaOrName;
|
|
178
|
+
if (!contract) throw new TypeError('Unknown enterprise contract.');
|
|
179
|
+
assertPlainData(value);
|
|
180
|
+
canonicalizeJson(value);
|
|
181
|
+
const errors = validateJson(value, contract);
|
|
182
|
+
// Do not include field values: validation responses must not echo private content.
|
|
183
|
+
if (errors.length) throw new TypeError(`Invalid ${contract['x-openplanr-contract']?.id ?? 'enterprise data'}: ${errors.slice(0, 5).map(error => `${error.path} (${error.rule})`).join('; ')}`);
|
|
184
|
+
assertTimes(value, contract);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Metadata immutability requires the storage adapter to compare-and-set IDs and recompute content digests. */
|
|
189
|
+
export function assertEnterpriseRevision(value) {
|
|
190
|
+
assertEnterpriseContract(value, ENTERPRISE_ARTIFACT_REVISION_SCHEMA);
|
|
191
|
+
if (value.id === value.parentRevisionId) throw new TypeError('A revision cannot be its own parent.');
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Verify append identity and bytes; the adapter still performs the atomic compare-and-set. */
|
|
196
|
+
export function assertEnterpriseRevisionAppend(revision, previous, content) {
|
|
197
|
+
assertEnterpriseRevision(revision);
|
|
198
|
+
if (previous) {
|
|
199
|
+
assertEnterpriseRevision(previous);
|
|
200
|
+
if (!sameScope(revision, previous) || revision.artifactId !== previous.artifactId || revision.parentRevisionId !== previous.id || revision.id === previous.id) throw new TypeError('Revision append does not match the current artifact revision.');
|
|
201
|
+
if (Date.parse(revision.createdAt) < Date.parse(previous.createdAt)) throw new TypeError('Revision append cannot precede its parent.');
|
|
202
|
+
} else if (revision.parentRevisionId !== null) throw new TypeError('An initial revision cannot have a parent.');
|
|
203
|
+
const bytes = typeof content === 'string' ? new TextEncoder().encode(content) : content;
|
|
204
|
+
if (!(bytes instanceof Uint8Array) || revision.byteLength !== bytes.byteLength || revision.contentDigest !== sha256Hex(bytes)) throw new TypeError('Revision digest or size does not match its content.');
|
|
205
|
+
return revision;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function assertEnterpriseReviewThread(value) {
|
|
209
|
+
assertEnterpriseContract(value, ENTERPRISE_REVIEW_THREAD_SCHEMA);
|
|
210
|
+
distinct(value.replies, 'id', 'reply identity');
|
|
211
|
+
if (value.replies.some(reply => reply.id === value.id)) throw new TypeError('A reply cannot reuse its thread identity.');
|
|
212
|
+
const start = Date.parse(value.createdAt), end = Date.parse(value.updatedAt);
|
|
213
|
+
if (end < start || value.replies.some(reply => Date.parse(reply.createdAt) < start || Date.parse(reply.createdAt) > end)) throw new TypeError('Review timestamps must stay within the thread lifetime.');
|
|
214
|
+
if (value.resolvedAt && (Date.parse(value.resolvedAt) < start || Date.parse(value.resolvedAt) > end)) throw new TypeError('Resolution timestamp must stay within the thread lifetime.');
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function assertEnterpriseProposal(value) {
|
|
219
|
+
assertEnterpriseContract(value, ENTERPRISE_CHANGE_PROPOSAL_SCHEMA);
|
|
220
|
+
if (new TextEncoder().encode(canonicalizeJson(value)).length > 5 * 1024 * 1024) throw new TypeError('Change proposal exceeds the content limit.');
|
|
221
|
+
if (value.validation.status === 'passed' && value.validation.issues.length) throw new TypeError('A passed validation cannot contain unresolved issues.');
|
|
222
|
+
if (value.operations.some(operation => operation.op === 'replace-document') && value.operations.length !== 1) throw new TypeError('Document replacement must be the only proposed operation.');
|
|
223
|
+
if (value.application && (value.application.revisionId === value.baseRevisionId || Date.parse(value.application.appliedAt) < Date.parse(value.createdAt))) throw new TypeError('Application must identify a new revision after proposal creation.');
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function assertEnterpriseEvidence(value) {
|
|
228
|
+
assertEnterpriseContract(value, ENTERPRISE_EVIDENCE_REFERENCE_SCHEMA);
|
|
229
|
+
if (value.source.kind === 'repository' && !isEnterpriseRepositoryPath(value.source.path)) throw new TypeError('Evidence requires a repository-relative path.');
|
|
230
|
+
if (value.source.kind === 'url') {
|
|
231
|
+
let url;
|
|
232
|
+
try { url = new URL(value.source.url); } catch { throw new TypeError('Evidence requires an HTTPS URL.'); }
|
|
233
|
+
if (url.protocol !== 'https:' || url.username || url.password) throw new TypeError('Evidence requires an HTTPS URL without credentials.');
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** A lexical check only; file application must also reject symlinks outside its repository root. */
|
|
239
|
+
export function isEnterpriseRepositoryPath(value) {
|
|
240
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 4096 &&
|
|
241
|
+
!/^[A-Za-z]:|^\/|[\\\x00-\x1f\x7f]/u.test(value) &&
|
|
242
|
+
value.split('/').every(part => part && part !== '.' && part !== '..' && !unsafeKeys.has(part));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function assertEnterpriseSync(value) {
|
|
246
|
+
assertEnterpriseContract(value, ENTERPRISE_SYNC_STATE_SCHEMA);
|
|
247
|
+
distinct(value.items, 'artifactId', 'sync artifact identity');
|
|
248
|
+
const scope = new Set(value.scope);
|
|
249
|
+
if (value.items.some(item => !scope.has(item.artifactId)) || value.issues.some(issue => issue.artifactId && !scope.has(issue.artifactId))) throw new TypeError('Synchronization cannot expand the selected scope.');
|
|
250
|
+
if (value.status === 'synchronized' && (value.issues.length || value.items.some(item => item.action === 'conflict'))) throw new TypeError('A conflicted synchronization cannot be marked synchronized.');
|
|
251
|
+
for (const item of value.items) {
|
|
252
|
+
if (item.action === 'create' && item.baseRevisionId !== null) throw new TypeError('Creation cannot replace an existing base revision.');
|
|
253
|
+
if (['update', 'unchanged'].includes(item.action) && item.baseRevisionId === null) throw new TypeError('An existing artifact needs a base revision.');
|
|
254
|
+
if (item.action === 'unchanged' && item.baseRevisionId !== item.revisionId) throw new TypeError('An unchanged artifact must retain its revision.');
|
|
255
|
+
if (value.status === 'synchronized' && item.revisionId === null) throw new TypeError('A synchronized artifact needs a revision.');
|
|
256
|
+
}
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const projectCapabilities = deepFreeze({
|
|
261
|
+
maintainer: ['project.manage', 'artifact.read', 'artifact.author', 'artifact.export', 'review.write', 'review.resolve'],
|
|
262
|
+
author: ['artifact.read', 'artifact.author', 'artifact.export', 'review.write', 'review.resolve'],
|
|
263
|
+
reviewer: ['artifact.read', 'artifact.export', 'review.write'],
|
|
264
|
+
viewer: ['artifact.read', 'artifact.export'],
|
|
265
|
+
});
|
|
266
|
+
const noCapabilities = Object.freeze([]);
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Stable UI projection for a server-verified project role. Callers must still
|
|
270
|
+
* authorize every request at the resource boundary; this only describes which
|
|
271
|
+
* controls the verified response may expose.
|
|
272
|
+
*/
|
|
273
|
+
export function enterpriseProjectCapabilities(role) {
|
|
274
|
+
return projectCapabilities[role] ?? noCapabilities;
|
|
275
|
+
}
|
|
276
|
+
const organizationCapabilities = deepFreeze({
|
|
277
|
+
owner: ['organization.manage', 'ownership.transfer', 'billing.manage', 'audit.read', 'project.create'],
|
|
278
|
+
admin: ['organization.manage', 'billing.manage', 'audit.read', 'project.create'], member: [],
|
|
279
|
+
});
|
|
280
|
+
const decision = (allowed, code) => Object.freeze({ allowed, code });
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Pure fail-closed policy. The adapter must load current membership/grants itself
|
|
284
|
+
* and verify actor identity. Never pass actor/ACL records supplied by a client.
|
|
285
|
+
* Organization administrators do not implicitly gain project content access.
|
|
286
|
+
*/
|
|
287
|
+
export function authorizeEnterpriseAccess(context) {
|
|
288
|
+
try { assertEnterpriseContract(context, ENTERPRISE_ACCESS_CONTEXT_SCHEMA); } catch { return decision(false, 'invalid-context'); }
|
|
289
|
+
const { actor, organization, membership, projectMembership, guestGrant, resource, action, now } = context;
|
|
290
|
+
if (!actor.verified) return decision(false, 'unverified-actor');
|
|
291
|
+
if (organization.id !== resource.organizationId) return decision(false, 'organization-mismatch');
|
|
292
|
+
if (organization.status !== 'active') return decision(false, 'organization-inactive');
|
|
293
|
+
if (resource.artifactId && !resource.projectId) return decision(false, 'project-required');
|
|
294
|
+
if (membership && (membership.actorId !== actor.id || membership.organizationId !== organization.id)) return decision(false, 'membership-mismatch');
|
|
295
|
+
if (membership && membership.status !== 'active') return decision(false, 'membership-revoked');
|
|
296
|
+
if (action.startsWith('organization.') || ['ownership.transfer', 'billing.manage', 'audit.read', 'project.create'].includes(action)) {
|
|
297
|
+
if (resource.projectId || resource.artifactId) return decision(false, 'organization-resource-required');
|
|
298
|
+
return membership && organizationCapabilities[membership.role].includes(action) ? decision(true, 'allowed') : decision(false, 'insufficient-role');
|
|
299
|
+
}
|
|
300
|
+
if (!resource.projectId) return decision(false, 'project-required');
|
|
301
|
+
if (projectMembership) {
|
|
302
|
+
if (projectMembership.actorId !== actor.id || !sameScope(projectMembership, resource)) return decision(false, 'project-membership-mismatch');
|
|
303
|
+
if (projectMembership.status !== 'active') return decision(false, 'project-membership-revoked');
|
|
304
|
+
if (!membership) return decision(false, 'membership-required');
|
|
305
|
+
return enterpriseProjectCapabilities(projectMembership.role).includes(action) ? decision(true, 'allowed') : decision(false, 'insufficient-role');
|
|
306
|
+
}
|
|
307
|
+
if (!guestGrant) return decision(false, 'project-membership-required');
|
|
308
|
+
if (!resource.artifactId || !sameScope(guestGrant, resource) || guestGrant.artifactId !== resource.artifactId || guestGrant.actorId !== actor.id) return decision(false, 'guest-scope-mismatch');
|
|
309
|
+
if (guestGrant.status !== 'active' || Date.parse(guestGrant.expiresAt) <= Date.parse(now)) return decision(false, 'guest-grant-expired-or-revoked');
|
|
310
|
+
return enterpriseProjectCapabilities(guestGrant.role).includes(action) ? decision(true, 'allowed') : decision(false, 'insufficient-role');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function assertEnterpriseHandoff(value) {
|
|
314
|
+
assertEnterpriseContract(value, ENTERPRISE_AGENT_HANDOFF_SCHEMA);
|
|
315
|
+
distinct(value.threads, 'id', 'thread identity'); distinct(value.evidence, 'id', 'evidence identity');
|
|
316
|
+
for (const thread of value.threads) {
|
|
317
|
+
assertEnterpriseReviewThread(thread);
|
|
318
|
+
if (!sameScope(thread, value) || thread.artifactId !== value.artifactId) throw new TypeError('Handoff threads must belong to its artifact and project.');
|
|
319
|
+
if (Date.parse(thread.updatedAt) > Date.parse(value.generatedAt)) throw new TypeError('Handoff cannot precede the exported feedback.');
|
|
320
|
+
}
|
|
321
|
+
for (const evidence of value.evidence) {
|
|
322
|
+
assertEnterpriseEvidence(evidence);
|
|
323
|
+
if (!sameScope(evidence, value)) throw new TypeError('Handoff evidence must belong to its project.');
|
|
324
|
+
if (Date.parse(evidence.capturedAt) > Date.parse(value.generatedAt)) throw new TypeError('Handoff cannot precede the exported evidence.');
|
|
325
|
+
}
|
|
326
|
+
const { contentDigest, ...content } = value;
|
|
327
|
+
if (sha256Hex(canonicalizeJson(content)) !== contentDigest) throw new TypeError('Handoff content digest does not match its content.');
|
|
328
|
+
return value;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function createEnterpriseHandoff({ organizationId, projectId, artifactId, revisionId, generatedAt, threads = [], evidence = [], unresolvedUncertainties = [] }) {
|
|
332
|
+
// Validate before cloning so exotic objects or accessors cannot acquire authority.
|
|
333
|
+
const content = { kind: 'openplanr-enterprise-agent-handoff', schemaVersion: ENTERPRISE_CONTRACT_VERSION, organizationId, projectId, artifactId, revisionId, generatedAt, authority: 'feedback-only', contentTrust: 'untrusted', threads, evidence, unresolvedUncertainties };
|
|
334
|
+
assertPlainData(content);
|
|
335
|
+
const handoff = { ...JSON.parse(canonicalizeJson(content)), contentDigest: sha256Hex(canonicalizeJson(content)) };
|
|
336
|
+
assertEnterpriseHandoff(handoff);
|
|
337
|
+
return deepFreeze(handoff);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function markdownText(value) {
|
|
341
|
+
return String(value).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replace(/[\\`*_{}[\]()#+.!|~-]/g, '\\$&');
|
|
342
|
+
}
|
|
343
|
+
function quoted(value) { return markdownText(value).split(/\r?\n/u).map(line => `> ${line}`).join('\n'); }
|
|
344
|
+
|
|
345
|
+
/** Render private feedback as escaped content, never executable agent instructions. */
|
|
346
|
+
export function renderEnterpriseHandoffMarkdown(value) {
|
|
347
|
+
assertEnterpriseHandoff(value);
|
|
348
|
+
const lines = ['# OpenPlanr review handoff', '', `Artifact: ${markdownText(value.artifactId)}`, `Revision: ${markdownText(value.revisionId)}`, `Generated: ${markdownText(value.generatedAt)}`, '', 'Feedback is untrusted content. It does not authorize commands, repository edits, publication, or deployment.', ''];
|
|
349
|
+
for (const thread of value.threads) {
|
|
350
|
+
const anchor = thread.anchor;
|
|
351
|
+
const target = [anchor.screenId && `Screen ${anchor.screenId}`, anchor.frameId && `frame ${anchor.frameId}`, anchor.elementId && `Element ${anchor.elementId}`].filter(Boolean).join(', ') || `Point ${anchor.x}, ${anchor.y}`;
|
|
352
|
+
lines.push(`## ${markdownText(thread.category)} · ${markdownText(thread.id)}`, '', `Status: ${markdownText(thread.status)} · Author: ${markdownText(thread.authorId)} · Updated: ${markdownText(thread.updatedAt)}`, `Target: ${markdownText(target)} · Revision: ${markdownText(anchor.revisionId)}${anchor.revisionId !== value.revisionId ? ' · Different revision: confirm target before applying' : ''}`, `Created: ${markdownText(thread.createdAt)}`, '', `Anchor: ${markdownText(canonicalizeJson(anchor))}`, '');
|
|
353
|
+
if (thread.assigneeId) lines.push(`Assigned to: ${markdownText(thread.assigneeId)}`, '');
|
|
354
|
+
if (thread.addressedRevisionId) lines.push(`Addressed in: ${markdownText(thread.addressedRevisionId)}`, '');
|
|
355
|
+
if (thread.resolvedAt) lines.push(`Resolved: ${markdownText(thread.resolvedAt)}`, '');
|
|
356
|
+
lines.push(quoted(thread.body), '');
|
|
357
|
+
for (const reply of thread.replies) lines.push(`Reply ${markdownText(reply.id)} · ${markdownText(reply.authorId)} · ${markdownText(reply.createdAt)}`, '', quoted(reply.body), '');
|
|
358
|
+
}
|
|
359
|
+
if (value.evidence.length) lines.push('## Evidence', '');
|
|
360
|
+
for (const evidence of value.evidence) lines.push(`- ${markdownText(evidence.label)} · ${markdownText(evidence.freshness)} · Captured ${markdownText(evidence.capturedAt)}`, '', quoted(canonicalizeJson(evidence.source)), '');
|
|
361
|
+
if (value.unresolvedUncertainties.length) lines.push('## Unresolved uncertainties', '');
|
|
362
|
+
for (const uncertainty of value.unresolvedUncertainties) lines.push(quoted(uncertainty), '');
|
|
363
|
+
return `${lines.join('\n').trim()}\n`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const editableCollections = Object.freeze(['nodes', 'relations', 'groups', 'events', 'items']);
|
|
367
|
+
const identityCollections = Object.freeze([...editableCollections, 'lanes', 'series', 'axes', 'sets', 'annotations']);
|
|
368
|
+
const copyData = value => JSON.parse(canonicalizeJson(value));
|
|
369
|
+
function assertDocument(value) {
|
|
370
|
+
assertPlainData(value);
|
|
371
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError('Semantic document must be a JSON object.');
|
|
372
|
+
canonicalizeJson(value);
|
|
373
|
+
}
|
|
374
|
+
function indexedElements(document) {
|
|
375
|
+
const entries = new Map();
|
|
376
|
+
const add = (targetId, element) => {
|
|
377
|
+
if (typeof targetId !== 'string' || validateJson(targetId, semanticId).length || unsafeKeys.has(targetId)) throw new TypeError('Invalid semantic element identity.');
|
|
378
|
+
if (entries.has(targetId)) throw new TypeError('Ambiguous or duplicate semantic element identity.');
|
|
379
|
+
entries.set(targetId, element);
|
|
380
|
+
};
|
|
381
|
+
const rootId = document.id ?? document.diagramId;
|
|
382
|
+
if (rootId !== undefined) add(rootId, document);
|
|
383
|
+
for (const collection of identityCollections) {
|
|
384
|
+
if (document[collection] === undefined) continue;
|
|
385
|
+
if (!Array.isArray(document[collection])) throw new TypeError('Semantic element collections must be arrays.');
|
|
386
|
+
for (const element of document[collection]) {
|
|
387
|
+
if (!element || typeof element !== 'object' || Array.isArray(element)) throw new TypeError('Semantic elements must be JSON objects.');
|
|
388
|
+
if (element.id !== undefined) add(element.id, element);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return entries;
|
|
392
|
+
}
|
|
393
|
+
function assertLayout(layout, entries) {
|
|
394
|
+
assertPlainData(layout);
|
|
395
|
+
if (!layout || typeof layout !== 'object' || Array.isArray(layout)) throw new TypeError('Layout must be an element-coordinate map.');
|
|
396
|
+
const coordinateSchema = closed({ x: { type: 'number', minimum: -1e7, maximum: 1e7 }, y: { type: 'number', minimum: -1e7, maximum: 1e7 } });
|
|
397
|
+
for (const [targetId, point] of Object.entries(layout)) {
|
|
398
|
+
if (!entries.has(targetId) || validateJson(point, coordinateSchema).length) throw new TypeError('Layout must identify existing elements and finite coordinates.');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const equalData = (left, right) => canonicalizeJson(left) === canonicalizeJson(right);
|
|
402
|
+
|
|
403
|
+
/** Compare stable element identities; documentDigest is derived metadata, not a semantic change. */
|
|
404
|
+
export function diffEnterpriseDocuments(before, after, { beforeLayout = {}, afterLayout = {} } = {}) {
|
|
405
|
+
assertDocument(before); assertDocument(after);
|
|
406
|
+
const previousEntries = indexedElements(before), nextEntries = indexedElements(after);
|
|
407
|
+
assertLayout(beforeLayout, previousEntries); assertLayout(afterLayout, nextEntries);
|
|
408
|
+
const changes = [];
|
|
409
|
+
const delta = (left, right, descriptor, hasBefore = true, hasAfter = true) => {
|
|
410
|
+
if (hasBefore && hasAfter && equalData(left, right)) return;
|
|
411
|
+
changes.push({ ...descriptor, op: !hasBefore ? 'add' : !hasAfter ? 'remove' : 'replace', ...(hasBefore ? { before: copyData(left) } : {}), ...(hasAfter ? { after: copyData(right) } : {}) });
|
|
412
|
+
};
|
|
413
|
+
for (const field of [...new Set([...Object.keys(before), ...Object.keys(after)])].sort()) {
|
|
414
|
+
if (field === 'documentDigest') continue;
|
|
415
|
+
const hasBefore = Object.hasOwn(before, field), hasAfter = Object.hasOwn(after, field);
|
|
416
|
+
if (identityCollections.includes(field) && hasBefore && hasAfter && before[field].every(item => typeof item.id === 'string') && after[field].every(item => typeof item.id === 'string')) {
|
|
417
|
+
const left = new Map(before[field].map(item => [item.id, item])), right = new Map(after[field].map(item => [item.id, item]));
|
|
418
|
+
for (const targetId of [...new Set([...left.keys(), ...right.keys()])].sort()) delta(left.get(targetId), right.get(targetId), { plane: 'semantic', collection: field, targetId }, left.has(targetId), right.has(targetId));
|
|
419
|
+
// Ordering matters for timelines and authored reading sequences.
|
|
420
|
+
delta([...left.keys()], [...right.keys()], { plane: 'semantic', field: `${field}.order` });
|
|
421
|
+
} else delta(before[field], after[field], { plane: field === 'layout' ? 'layout' : 'semantic', field }, hasBefore, hasAfter);
|
|
422
|
+
}
|
|
423
|
+
for (const targetId of [...new Set([...Object.keys(beforeLayout), ...Object.keys(afterLayout)])].sort()) delta(beforeLayout[targetId], afterLayout[targetId], { plane: 'layout', targetId }, Object.hasOwn(beforeLayout, targetId), Object.hasOwn(afterLayout, targetId));
|
|
424
|
+
return { semanticChanged: changes.some(change => change.plane === 'semantic'), layoutChanged: changes.some(change => change.plane === 'layout'), changes };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Deterministic in-memory proposal preview. No authority or repository writes.
|
|
429
|
+
* Callers must check the base revision, authorize application, validate the final
|
|
430
|
+
* domain document (including all references), and restamp its derived digest.
|
|
431
|
+
*/
|
|
432
|
+
export function applyEnterpriseOperations(document, proposal, { layout = {} } = {}) {
|
|
433
|
+
assertDocument(document); assertEnterpriseProposal(proposal);
|
|
434
|
+
let next = copyData(document), nextLayout = copyData(layout);
|
|
435
|
+
assertLayout(nextLayout, indexedElements(next));
|
|
436
|
+
for (const operation of proposal.operations) {
|
|
437
|
+
const entries = indexedElements(next);
|
|
438
|
+
if (operation.op === 'replace-document') {
|
|
439
|
+
next = copyData(operation.content);
|
|
440
|
+
const newEntries = indexedElements(next);
|
|
441
|
+
nextLayout = Object.fromEntries(Object.entries(nextLayout).filter(([targetId]) => newEntries.has(targetId)));
|
|
442
|
+
} else if (operation.op === 'add-element') {
|
|
443
|
+
if (entries.has(operation.element.id)) throw new TypeError('Cannot add a duplicate semantic element.');
|
|
444
|
+
next[operation.collection] ??= [];
|
|
445
|
+
if (!Array.isArray(next[operation.collection])) throw new TypeError('Cannot add to a non-array collection.');
|
|
446
|
+
next[operation.collection].push(copyData(operation.element));
|
|
447
|
+
} else if (operation.op === 'remove-element') {
|
|
448
|
+
const items = next[operation.collection];
|
|
449
|
+
if (!Array.isArray(items) || !items.some(item => item.id === operation.targetId)) throw new TypeError('Cannot remove an absent semantic element.');
|
|
450
|
+
next[operation.collection] = items.filter(item => item.id !== operation.targetId);
|
|
451
|
+
delete nextLayout[operation.targetId];
|
|
452
|
+
} else {
|
|
453
|
+
const target = entries.get(operation.targetId);
|
|
454
|
+
if (!target) throw new TypeError('Operation target is absent from the semantic document.');
|
|
455
|
+
if (operation.op === 'set-layout') nextLayout[operation.targetId] = { x: operation.x, y: operation.y };
|
|
456
|
+
else target[operation.field] = operation.value;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Detect ambiguous identities introduced by additions or document replacement.
|
|
460
|
+
indexedElements(next);
|
|
461
|
+
const result = diffEnterpriseDocuments(document, next, { beforeLayout: layout, afterLayout: nextLayout });
|
|
462
|
+
return { document: next, layout: nextLayout, ...result };
|
|
463
|
+
}
|
package/src/errors.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const ARTIFACT_ERROR_CODES: Readonly<Record<string, string>>;
|
|
2
|
+
export declare const PIPELINE_ERROR_CODES: Readonly<Record<string, string>>;
|
|
3
|
+
export declare const PROTOCOL_ERROR_CODES: Readonly<Record<string, string>>;
|
|
4
|
+
export declare class PipelineError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly fix: string;
|
|
7
|
+
readonly details?: unknown;
|
|
8
|
+
constructor(code: string, message: string, fix?: string, details?: unknown);
|
|
9
|
+
toJSON(): { ok: false; code: string; problem: string; fix?: string; details?: unknown };
|
|
10
|
+
}
|
|
11
|
+
export declare class ProtocolError extends PipelineError {}
|
package/src/errors.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export const ARTIFACT_ERROR_CODES = Object.freeze({
|
|
2
|
+
INPUT_INVALID: 'E_ARTIFACT_INPUT_INVALID',
|
|
3
|
+
FILE_MISSING: 'E_ARTIFACT_FILE_MISSING',
|
|
4
|
+
ROOT_MISSING: 'E_ARTIFACT_ROOT_MISSING',
|
|
5
|
+
PATH_TRAVERSAL: 'E_ARTIFACT_PATH_TRAVERSAL',
|
|
6
|
+
SYMLINK_ESCAPE: 'E_ARTIFACT_SYMLINK_ESCAPE',
|
|
7
|
+
EXTERNAL_ASSET: 'E_ARTIFACT_EXTERNAL_ASSET',
|
|
8
|
+
UNRESOLVED_ASSET: 'E_ARTIFACT_UNRESOLVED_ASSET',
|
|
9
|
+
UNSUPPORTED_MODULE: 'E_ARTIFACT_UNSUPPORTED_MODULE',
|
|
10
|
+
UNSAFE_HTML: 'E_ARTIFACT_UNSAFE_HTML',
|
|
11
|
+
FILE_LIMIT: 'E_ARTIFACT_FILE_LIMIT',
|
|
12
|
+
BYTE_LIMIT: 'E_ARTIFACT_BYTE_LIMIT',
|
|
13
|
+
OUTPUT_LIMIT: 'E_ARTIFACT_OUTPUT_LIMIT',
|
|
14
|
+
REDACTION: 'E_ARTIFACT_REDACTION',
|
|
15
|
+
ENVELOPE_INVALID: 'E_ARTIFACT_ENVELOPE_INVALID',
|
|
16
|
+
SCHEMA_UNSUPPORTED: 'E_ARTIFACT_SCHEMA_UNSUPPORTED',
|
|
17
|
+
LOOPBACK_BIND: 'E_ARTIFACT_LOOPBACK_BIND',
|
|
18
|
+
LOOPBACK_STATE: 'E_ARTIFACT_LOOPBACK_STATE',
|
|
19
|
+
PORT_IN_USE: 'E_ARTIFACT_PORT_IN_USE',
|
|
20
|
+
SESSION_TOKEN: 'E_ARTIFACT_SESSION_TOKEN',
|
|
21
|
+
SESSION_NOT_FOUND: 'E_ARTIFACT_SESSION_NOT_FOUND',
|
|
22
|
+
REQUEST_INVALID: 'E_ARTIFACT_REQUEST_INVALID',
|
|
23
|
+
REQUEST_LIMIT: 'E_ARTIFACT_REQUEST_LIMIT',
|
|
24
|
+
SANDBOX_POLICY: 'E_ARTIFACT_SANDBOX_POLICY',
|
|
25
|
+
BRIDGE_INVALID: 'E_ARTIFACT_BRIDGE_INVALID',
|
|
26
|
+
REVIEW_INVALID: 'E_ARTIFACT_REVIEW_INVALID',
|
|
27
|
+
REVIEW_WRITE: 'E_ARTIFACT_REVIEW_WRITE',
|
|
28
|
+
REVIEW_IMPORT: 'E_ARTIFACT_REVIEW_IMPORT',
|
|
29
|
+
REVIEW_DECODER_REQUIRED: 'E_ARTIFACT_REVIEW_DECODER_REQUIRED',
|
|
30
|
+
REVIEW_EXPORT: 'E_ARTIFACT_REVIEW_EXPORT',
|
|
31
|
+
DIGEST_MISMATCH: 'E_ARTIFACT_DIGEST_MISMATCH',
|
|
32
|
+
STALE_REVIEW: 'E_ARTIFACT_STALE_REVIEW',
|
|
33
|
+
MERGE_CONFLICT: 'E_ARTIFACT_MERGE_CONFLICT',
|
|
34
|
+
CODEC_INVALID: 'E_ARTIFACT_CODEC_INVALID',
|
|
35
|
+
CODEC_UNAVAILABLE: 'E_ARTIFACT_CODEC_UNAVAILABLE',
|
|
36
|
+
CODEC_FAILED: 'E_ARTIFACT_CODEC_FAILED',
|
|
37
|
+
FRAGMENT_INVALID: 'E_ARTIFACT_FRAGMENT_INVALID',
|
|
38
|
+
FRAGMENT_VERSION_UNSUPPORTED: 'E_ARTIFACT_FRAGMENT_VERSION_UNSUPPORTED',
|
|
39
|
+
FRAGMENT_TOO_LARGE: 'E_ARTIFACT_FRAGMENT_TOO_LARGE',
|
|
40
|
+
DECOMPRESSION_LIMIT: 'E_ARTIFACT_DECOMPRESSION_LIMIT',
|
|
41
|
+
BROWSER_UNSUPPORTED: 'E_ARTIFACT_BROWSER_UNSUPPORTED',
|
|
42
|
+
ENCRYPTION_FAILED: 'E_ARTIFACT_ENCRYPTION_FAILED',
|
|
43
|
+
DECRYPTION_FAILED: 'E_ARTIFACT_DECRYPTION_FAILED',
|
|
44
|
+
CONFIRMATION_REQUIRED: 'E_ARTIFACT_CONFIRMATION_REQUIRED',
|
|
45
|
+
SHORT_CONFIRMATION_REQUIRED: 'E_ARTIFACT_SHORT_CONFIRMATION_REQUIRED',
|
|
46
|
+
PASTE_INVALID: 'E_ARTIFACT_PASTE_INVALID',
|
|
47
|
+
PASTE_UNAVAILABLE: 'E_ARTIFACT_PASTE_UNAVAILABLE',
|
|
48
|
+
PASTE_EXPIRED: 'E_ARTIFACT_PASTE_EXPIRED',
|
|
49
|
+
PASTE_LIMIT: 'E_ARTIFACT_PASTE_LIMIT',
|
|
50
|
+
SHARE_NETWORK: 'E_ARTIFACT_SHARE_NETWORK',
|
|
51
|
+
ROOM_CREATE_AMBIGUOUS: 'E_ARTIFACT_ROOM_CREATE_AMBIGUOUS',
|
|
52
|
+
ROOM_INVALID: 'E_ARTIFACT_ROOM_INVALID',
|
|
53
|
+
ROOM_UNAVAILABLE: 'E_ARTIFACT_ROOM_UNAVAILABLE',
|
|
54
|
+
ROOM_EXPIRED: 'E_ARTIFACT_ROOM_EXPIRED',
|
|
55
|
+
ROOM_CLOSED: 'E_ARTIFACT_ROOM_CLOSED',
|
|
56
|
+
ROOM_LEGACY_READ_ONLY: 'E_ARTIFACT_ROOM_LEGACY_READ_ONLY',
|
|
57
|
+
ROOM_FORBIDDEN: 'E_ARTIFACT_ROOM_FORBIDDEN',
|
|
58
|
+
ROOM_EVENT_INVALID: 'E_ARTIFACT_ROOM_EVENT_INVALID',
|
|
59
|
+
ROOM_EVENT_REPLAY: 'E_ARTIFACT_ROOM_EVENT_REPLAY',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const PROTOCOL_ERROR_CODES = Object.freeze({
|
|
63
|
+
ASSET_NOT_FOUND: 'E_PROTOCOL_ASSET_NOT_FOUND',
|
|
64
|
+
DIGEST_MISMATCH: 'E_PROTOCOL_DIGEST_MISMATCH',
|
|
65
|
+
DOCUMENT_INVALID: 'E_PROTOCOL_DOCUMENT_INVALID',
|
|
66
|
+
DOCUMENT_VERSION_UNSUPPORTED: 'E_PROTOCOL_DOCUMENT_VERSION_UNSUPPORTED',
|
|
67
|
+
DUPLICATE_ID: 'E_PROTOCOL_DUPLICATE_ID',
|
|
68
|
+
REFERENCE_INVALID: 'E_PROTOCOL_REFERENCE_INVALID',
|
|
69
|
+
REGISTRY_INVALID: 'E_PROTOCOL_REGISTRY_INVALID',
|
|
70
|
+
SCHEMA_INVALID: 'E_PROTOCOL_SCHEMA_INVALID',
|
|
71
|
+
SCHEMA_UNKNOWN: 'E_SCHEMA_UNKNOWN',
|
|
72
|
+
SCHEMA_VERSION_UNSUPPORTED: 'E_SCHEMA_VERSION_UNSUPPORTED',
|
|
73
|
+
SORT_ORDER_INVALID: 'E_PROTOCOL_SORT_ORDER_INVALID',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Compatibility name retained for consumers that imported the pipeline error
|
|
77
|
+
// vocabulary object rather than the PipelineError class.
|
|
78
|
+
export const PIPELINE_ERROR_CODES = PROTOCOL_ERROR_CODES;
|
|
79
|
+
|
|
80
|
+
export class PipelineError extends Error {
|
|
81
|
+
constructor(code, message, fix = '', details = undefined) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = 'PipelineError';
|
|
84
|
+
this.code = code;
|
|
85
|
+
this.fix = fix;
|
|
86
|
+
if (details !== undefined) this.details = details;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
toJSON() {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
code: this.code,
|
|
93
|
+
problem: this.message,
|
|
94
|
+
...(this.fix ? { fix: this.fix } : {}),
|
|
95
|
+
...(this.details === undefined ? {} : { details: this.details }),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Package-native typed error. PipelineError remains exported as the exact
|
|
102
|
+
* compatibility base used by the existing public pipeline package.
|
|
103
|
+
*/
|
|
104
|
+
export class ProtocolError extends PipelineError {
|
|
105
|
+
constructor(code, message, fix = '', details = undefined) {
|
|
106
|
+
super(code, message, fix, details);
|
|
107
|
+
this.name = 'ProtocolError';
|
|
108
|
+
}
|
|
109
|
+
}
|