@lingjingai/scriptctl 0.49.16 → 0.50.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/README.md +8 -2
- package/changes/0.50.0.md +63 -0
- package/changes/unreleased.md +2 -1
- package/dist/cli.js +26 -5
- package/dist/cli.js.map +1 -1
- package/dist/domain/authoring/apply.d.ts +1 -33
- package/dist/domain/authoring/apply.js +1 -783
- package/dist/domain/authoring/apply.js.map +1 -1
- package/dist/domain/authoring/export.d.ts +1 -7
- package/dist/domain/authoring/export.js +1 -84
- package/dist/domain/authoring/export.js.map +1 -1
- package/dist/domain/authoring/parser.d.ts +2 -3
- package/dist/domain/authoring/parser.js +2 -914
- package/dist/domain/authoring/parser.js.map +1 -1
- package/dist/domain/authoring/serializer.d.ts +1 -2
- package/dist/domain/authoring/serializer.js +1 -243
- package/dist/domain/authoring/serializer.js.map +1 -1
- package/dist/domain/authoring/shared/bracket.d.ts +4 -0
- package/dist/domain/authoring/shared/bracket.js +95 -0
- package/dist/domain/authoring/shared/bracket.js.map +1 -0
- package/dist/domain/authoring/shared/types.d.ts +12 -0
- package/dist/domain/authoring/shared/types.js +2 -0
- package/dist/domain/authoring/shared/types.js.map +1 -0
- package/dist/domain/authoring/types.d.ts +2 -87
- package/dist/domain/authoring/types.js +2 -1
- package/dist/domain/authoring/types.js.map +1 -1
- package/dist/domain/authoring/v3/apply.d.ts +33 -0
- package/dist/domain/authoring/v3/apply.js +784 -0
- package/dist/domain/authoring/v3/apply.js.map +1 -0
- package/dist/domain/authoring/v3/export.d.ts +7 -0
- package/dist/domain/authoring/v3/export.js +85 -0
- package/dist/domain/authoring/v3/export.js.map +1 -0
- package/dist/domain/authoring/v3/parser.d.ts +3 -0
- package/dist/domain/authoring/v3/parser.js +823 -0
- package/dist/domain/authoring/v3/parser.js.map +1 -0
- package/dist/domain/authoring/v3/serializer.d.ts +2 -0
- package/dist/domain/authoring/v3/serializer.js +244 -0
- package/dist/domain/authoring/v3/serializer.js.map +1 -0
- package/dist/domain/authoring/v3/types.d.ts +76 -0
- package/dist/domain/authoring/v3/types.js +2 -0
- package/dist/domain/authoring/v3/types.js.map +1 -0
- package/dist/domain/authoring/v4/apply.d.ts +40 -0
- package/dist/domain/authoring/v4/apply.js +451 -0
- package/dist/domain/authoring/v4/apply.js.map +1 -0
- package/dist/domain/authoring/v4/parser.d.ts +2 -0
- package/dist/domain/authoring/v4/parser.js +729 -0
- package/dist/domain/authoring/v4/parser.js.map +1 -0
- package/dist/domain/authoring/v4/serializer.d.ts +6 -0
- package/dist/domain/authoring/v4/serializer.js +280 -0
- package/dist/domain/authoring/v4/serializer.js.map +1 -0
- package/dist/domain/authoring/v4/types.d.ts +50 -0
- package/dist/domain/authoring/v4/types.js +2 -0
- package/dist/domain/authoring/v4/types.js.map +1 -0
- package/dist/domain/ingest/apply-plan.d.ts +1 -1
- package/dist/domain/ingest/assembler.d.ts +9 -0
- package/dist/domain/ingest/assembler.js +98 -36
- package/dist/domain/ingest/assembler.js.map +1 -1
- package/dist/domain/ingest/asset-audit.d.ts +50 -0
- package/dist/domain/ingest/asset-audit.js +196 -0
- package/dist/domain/ingest/asset-audit.js.map +1 -0
- package/dist/domain/ingest/production-validation.d.ts +5 -4
- package/dist/domain/ingest/production-validation.js +10 -25
- package/dist/domain/ingest/production-validation.js.map +1 -1
- package/dist/domain/ingest/records.d.ts +1 -1
- package/dist/domain/ingest/records.js +1 -1
- package/dist/domain/ingest/records.js.map +1 -1
- package/dist/domain/ingest/serial-context.d.ts +1 -1
- package/dist/domain/ingest/validation.d.ts +1 -11
- package/dist/domain/ingest/validation.js +1 -199
- package/dist/domain/ingest/validation.js.map +1 -1
- package/dist/domain/ingest/video-apply.js +68 -45
- package/dist/domain/ingest/video-apply.js.map +1 -1
- package/dist/domain/script/address.d.ts +1 -32
- package/dist/domain/script/address.js +1 -144
- package/dist/domain/script/address.js.map +1 -1
- package/dist/domain/script/dispatch.d.ts +14 -0
- package/dist/domain/script/dispatch.js +47 -0
- package/dist/domain/script/dispatch.js.map +1 -0
- package/dist/domain/script/lookups.d.ts +1 -33
- package/dist/domain/script/lookups.js +1 -316
- package/dist/domain/script/lookups.js.map +1 -1
- package/dist/domain/script/parse.d.ts +1 -3
- package/dist/domain/script/parse.js +1 -164
- package/dist/domain/script/parse.js.map +1 -1
- package/dist/domain/script/patch/apply.d.ts +1 -2
- package/dist/domain/script/patch/apply.js +1 -30
- package/dist/domain/script/patch/apply.js.map +1 -1
- package/dist/domain/script/patch/helpers.d.ts +1 -17
- package/dist/domain/script/patch/helpers.js +1 -210
- package/dist/domain/script/patch/helpers.js.map +1 -1
- package/dist/domain/script/patch/inline-speaker.d.ts +1 -2
- package/dist/domain/script/patch/inline-speaker.js +1 -49
- package/dist/domain/script/patch/inline-speaker.js.map +1 -1
- package/dist/domain/script/patch/ops-action.d.ts +1 -12
- package/dist/domain/script/patch/ops-action.js +1 -366
- package/dist/domain/script/patch/ops-action.js.map +1 -1
- package/dist/domain/script/patch/ops-asset.d.ts +1 -14
- package/dist/domain/script/patch/ops-asset.js +1 -364
- package/dist/domain/script/patch/ops-asset.js.map +1 -1
- package/dist/domain/script/patch/ops-dialogue.d.ts +1 -2
- package/dist/domain/script/patch/ops-dialogue.js +1 -25
- package/dist/domain/script/patch/ops-dialogue.js.map +1 -1
- package/dist/domain/script/patch/ops-extend.d.ts +1 -8
- package/dist/domain/script/patch/ops-extend.js +1 -110
- package/dist/domain/script/patch/ops-extend.js.map +1 -1
- package/dist/domain/script/patch/ops-meta.d.ts +1 -5
- package/dist/domain/script/patch/ops-meta.js +1 -81
- package/dist/domain/script/patch/ops-meta.js.map +1 -1
- package/dist/domain/script/patch/ops-scene.d.ts +1 -6
- package/dist/domain/script/patch/ops-scene.js +1 -350
- package/dist/domain/script/patch/ops-scene.js.map +1 -1
- package/dist/domain/script/patch/ops-state.d.ts +1 -8
- package/dist/domain/script/patch/ops-state.js +1 -164
- package/dist/domain/script/patch/ops-state.js.map +1 -1
- package/dist/domain/script/patch/registry.d.ts +1 -2
- package/dist/domain/script/patch/registry.js +1 -52
- package/dist/domain/script/patch/registry.js.map +1 -1
- package/dist/domain/script/patch/types.d.ts +1 -10
- package/dist/domain/script/patch/types.js +1 -1
- package/dist/domain/script/patch/types.js.map +1 -1
- package/dist/domain/script/refs.d.ts +1 -4
- package/dist/domain/script/refs.js +1 -63
- package/dist/domain/script/refs.js.map +1 -1
- package/dist/domain/script/scene-refs.d.ts +1 -3
- package/dist/domain/script/scene-refs.js +1 -34
- package/dist/domain/script/scene-refs.js.map +1 -1
- package/dist/domain/script/schema.d.ts +1 -6
- package/dist/domain/script/schema.js +1 -55
- package/dist/domain/script/schema.js.map +1 -1
- package/dist/domain/script/selector.d.ts +1 -45
- package/dist/domain/script/selector.js +1 -184
- package/dist/domain/script/selector.js.map +1 -1
- package/dist/domain/script/shared/regex.d.ts +2 -0
- package/dist/domain/script/shared/regex.js +7 -0
- package/dist/domain/script/shared/regex.js.map +1 -0
- package/dist/domain/script/shared/types.d.ts +18 -0
- package/dist/domain/script/shared/types.js +9 -0
- package/dist/domain/script/shared/types.js.map +1 -0
- package/dist/domain/script/shared/validation.d.ts +10 -0
- package/dist/domain/script/shared/validation.js +2 -0
- package/dist/domain/script/shared/validation.js.map +1 -0
- package/dist/domain/script/types.d.ts +1 -123
- package/dist/domain/script/types.js +1 -8
- package/dist/domain/script/types.js.map +1 -1
- package/dist/domain/script/v3/address.d.ts +32 -0
- package/dist/domain/script/v3/address.js +145 -0
- package/dist/domain/script/v3/address.js.map +1 -0
- package/dist/domain/script/v3/lookups.d.ts +33 -0
- package/dist/domain/script/v3/lookups.js +317 -0
- package/dist/domain/script/v3/lookups.js.map +1 -0
- package/dist/domain/script/v3/ordinal.d.ts +8 -0
- package/dist/domain/script/v3/ordinal.js +92 -0
- package/dist/domain/script/v3/ordinal.js.map +1 -0
- package/dist/domain/script/v3/parse.d.ts +3 -0
- package/dist/domain/script/v3/parse.js +165 -0
- package/dist/domain/script/v3/parse.js.map +1 -0
- package/dist/domain/script/v3/patch/apply.d.ts +2 -0
- package/dist/domain/script/v3/patch/apply.js +31 -0
- package/dist/domain/script/v3/patch/apply.js.map +1 -0
- package/dist/domain/script/v3/patch/helpers.d.ts +17 -0
- package/dist/domain/script/v3/patch/helpers.js +211 -0
- package/dist/domain/script/v3/patch/helpers.js.map +1 -0
- package/dist/domain/script/v3/patch/inline-speaker.d.ts +2 -0
- package/dist/domain/script/v3/patch/inline-speaker.js +50 -0
- package/dist/domain/script/v3/patch/inline-speaker.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-action.d.ts +12 -0
- package/dist/domain/script/v3/patch/ops-action.js +367 -0
- package/dist/domain/script/v3/patch/ops-action.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-asset.d.ts +14 -0
- package/dist/domain/script/v3/patch/ops-asset.js +365 -0
- package/dist/domain/script/v3/patch/ops-asset.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-dialogue.d.ts +2 -0
- package/dist/domain/script/v3/patch/ops-dialogue.js +26 -0
- package/dist/domain/script/v3/patch/ops-dialogue.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-extend.d.ts +8 -0
- package/dist/domain/script/v3/patch/ops-extend.js +111 -0
- package/dist/domain/script/v3/patch/ops-extend.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-meta.d.ts +5 -0
- package/dist/domain/script/v3/patch/ops-meta.js +82 -0
- package/dist/domain/script/v3/patch/ops-meta.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-scene.d.ts +6 -0
- package/dist/domain/script/v3/patch/ops-scene.js +351 -0
- package/dist/domain/script/v3/patch/ops-scene.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-state.d.ts +8 -0
- package/dist/domain/script/v3/patch/ops-state.js +165 -0
- package/dist/domain/script/v3/patch/ops-state.js.map +1 -0
- package/dist/domain/script/v3/patch/registry.d.ts +2 -0
- package/dist/domain/script/v3/patch/registry.js +53 -0
- package/dist/domain/script/v3/patch/registry.js.map +1 -0
- package/dist/domain/script/v3/patch/types.d.ts +10 -0
- package/dist/domain/script/v3/patch/types.js +2 -0
- package/dist/domain/script/v3/patch/types.js.map +1 -0
- package/dist/domain/script/v3/refs.d.ts +4 -0
- package/dist/domain/script/v3/refs.js +64 -0
- package/dist/domain/script/v3/refs.js.map +1 -0
- package/dist/domain/script/v3/scene-refs.d.ts +3 -0
- package/dist/domain/script/v3/scene-refs.js +35 -0
- package/dist/domain/script/v3/scene-refs.js.map +1 -0
- package/dist/domain/script/v3/schema.d.ts +6 -0
- package/dist/domain/script/v3/schema.js +56 -0
- package/dist/domain/script/v3/schema.js.map +1 -0
- package/dist/domain/script/v3/selector.d.ts +44 -0
- package/dist/domain/script/v3/selector.js +180 -0
- package/dist/domain/script/v3/selector.js.map +1 -0
- package/dist/domain/script/v3/types.d.ts +106 -0
- package/dist/domain/script/v3/types.js +2 -0
- package/dist/domain/script/v3/types.js.map +1 -0
- package/dist/domain/script/v3/validation.d.ts +3 -0
- package/dist/domain/script/v3/validation.js +200 -0
- package/dist/domain/script/v3/validation.js.map +1 -0
- package/dist/domain/script/v4/address.d.ts +61 -0
- package/dist/domain/script/v4/address.js +100 -0
- package/dist/domain/script/v4/address.js.map +1 -0
- package/dist/domain/script/v4/factory.d.ts +2 -0
- package/dist/domain/script/v4/factory.js +13 -0
- package/dist/domain/script/v4/factory.js.map +1 -0
- package/dist/domain/script/v4/from-v3.d.ts +29 -0
- package/dist/domain/script/v4/from-v3.js +307 -0
- package/dist/domain/script/v4/from-v3.js.map +1 -0
- package/dist/domain/script/v4/ids.d.ts +27 -0
- package/dist/domain/script/v4/ids.js +63 -0
- package/dist/domain/script/v4/ids.js.map +1 -0
- package/dist/domain/script/v4/ordinal.d.ts +3 -0
- package/dist/domain/script/v4/ordinal.js +67 -0
- package/dist/domain/script/v4/ordinal.js.map +1 -0
- package/dist/domain/script/v4/parse.d.ts +3 -0
- package/dist/domain/script/v4/parse.js +18 -0
- package/dist/domain/script/v4/parse.js.map +1 -0
- package/dist/domain/script/v4/patch/apply.d.ts +3 -0
- package/dist/domain/script/v4/patch/apply.js +24 -0
- package/dist/domain/script/v4/patch/apply.js.map +1 -0
- package/dist/domain/script/v4/patch/helpers.d.ts +32 -0
- package/dist/domain/script/v4/patch/helpers.js +139 -0
- package/dist/domain/script/v4/patch/helpers.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-action.d.ts +13 -0
- package/dist/domain/script/v4/patch/ops-action.js +379 -0
- package/dist/domain/script/v4/patch/ops-action.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-asset.d.ts +17 -0
- package/dist/domain/script/v4/patch/ops-asset.js +617 -0
- package/dist/domain/script/v4/patch/ops-asset.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-extend.d.ts +11 -0
- package/dist/domain/script/v4/patch/ops-extend.js +86 -0
- package/dist/domain/script/v4/patch/ops-extend.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-meta.d.ts +5 -0
- package/dist/domain/script/v4/patch/ops-meta.js +49 -0
- package/dist/domain/script/v4/patch/ops-meta.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.d.ts +3 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.js +98 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-structure.d.ts +6 -0
- package/dist/domain/script/v4/patch/ops-structure.js +236 -0
- package/dist/domain/script/v4/patch/ops-structure.js.map +1 -0
- package/dist/domain/script/v4/patch/registry.d.ts +2 -0
- package/dist/domain/script/v4/patch/registry.js +50 -0
- package/dist/domain/script/v4/patch/registry.js.map +1 -0
- package/dist/domain/script/v4/patch/schema.d.ts +7 -0
- package/dist/domain/script/v4/patch/schema.js +75 -0
- package/dist/domain/script/v4/patch/schema.js.map +1 -0
- package/dist/domain/script/v4/patch/types.d.ts +10 -0
- package/dist/domain/script/v4/patch/types.js +2 -0
- package/dist/domain/script/v4/patch/types.js.map +1 -0
- package/dist/domain/script/v4/selector.d.ts +11 -0
- package/dist/domain/script/v4/selector.js +125 -0
- package/dist/domain/script/v4/selector.js.map +1 -0
- package/dist/domain/script/v4/types.d.ts +120 -0
- package/dist/domain/script/v4/types.js +2 -0
- package/dist/domain/script/v4/types.js.map +1 -0
- package/dist/domain/script/v4/validation.d.ts +2 -0
- package/dist/domain/script/v4/validation.js +378 -0
- package/dist/domain/script/v4/validation.js.map +1 -0
- package/dist/help-text.js +146 -114
- package/dist/help-text.js.map +1 -1
- package/dist/llm/config.js +2 -0
- package/dist/llm/config.js.map +1 -1
- package/dist/llm/registry.d.ts +1 -0
- package/dist/llm/registry.js +3 -0
- package/dist/llm/registry.js.map +1 -1
- package/dist/llm/tasks/ingest/asset-audit.d.ts +6 -0
- package/dist/llm/tasks/ingest/asset-audit.js +164 -0
- package/dist/llm/tasks/ingest/asset-audit.js.map +1 -0
- package/dist/llm/tasks/ingest/asset-curate.d.ts +1 -1
- package/dist/llm/types.d.ts +1 -1
- package/dist/llm/types.js.map +1 -1
- package/dist/usecases/ingest/command.js +1 -1
- package/dist/usecases/ingest/final-asset-audit.d.ts +39 -0
- package/dist/usecases/ingest/final-asset-audit.js +88 -0
- package/dist/usecases/ingest/final-asset-audit.js.map +1 -0
- package/dist/usecases/ingest/pipeline.js +62 -11
- package/dist/usecases/ingest/pipeline.js.map +1 -1
- package/dist/usecases/ingest/serial-govern.d.ts +2 -1
- package/dist/usecases/ingest/serial-govern.js +11 -4
- package/dist/usecases/ingest/serial-govern.js.map +1 -1
- package/dist/usecases/ingest/video-pipeline.js +100 -14
- package/dist/usecases/ingest/video-pipeline.js.map +1 -1
- package/dist/usecases/ingest/view-server.js +2 -2
- package/dist/usecases/ingest/view-server.js.map +1 -1
- package/dist/usecases/script/actions.js +101 -5
- package/dist/usecases/script/actions.js.map +1 -1
- package/dist/usecases/script/actors.js +16 -7
- package/dist/usecases/script/actors.js.map +1 -1
- package/dist/usecases/script/alias.js +7 -6
- package/dist/usecases/script/alias.js.map +1 -1
- package/dist/usecases/script/assets.js +18 -2
- package/dist/usecases/script/assets.js.map +1 -1
- package/dist/usecases/script/audit-assets.d.ts +4 -0
- package/dist/usecases/script/audit-assets.js +40 -0
- package/dist/usecases/script/audit-assets.js.map +1 -0
- package/dist/usecases/script/command-constraints.d.ts +36 -0
- package/dist/usecases/script/command-constraints.js +115 -0
- package/dist/usecases/script/command-constraints.js.map +1 -0
- package/dist/usecases/script/context.js +31 -4
- package/dist/usecases/script/context.js.map +1 -1
- package/dist/usecases/script/create.js +2 -2
- package/dist/usecases/script/create.js.map +1 -1
- package/dist/usecases/script/delete.js +6 -3
- package/dist/usecases/script/delete.js.map +1 -1
- package/dist/usecases/script/describe.js +7 -4
- package/dist/usecases/script/describe.js.map +1 -1
- package/dist/usecases/script/dialogue.js +15 -5
- package/dist/usecases/script/dialogue.js.map +1 -1
- package/dist/usecases/script/dispatch.d.ts +3 -0
- package/dist/usecases/script/dispatch.js +10 -0
- package/dist/usecases/script/dispatch.js.map +1 -0
- package/dist/usecases/script/do.js +74 -11
- package/dist/usecases/script/do.js.map +1 -1
- package/dist/usecases/script/emotion.js +14 -3
- package/dist/usecases/script/emotion.js.map +1 -1
- package/dist/usecases/script/episodes.js +15 -4
- package/dist/usecases/script/episodes.js.map +1 -1
- package/dist/usecases/script/export-authoring.js +35 -11
- package/dist/usecases/script/export-authoring.js.map +1 -1
- package/dist/usecases/script/extend.js +23 -15
- package/dist/usecases/script/extend.js.map +1 -1
- package/dist/usecases/script/ids.js +53 -10
- package/dist/usecases/script/ids.js.map +1 -1
- package/dist/usecases/script/insert.js +74 -38
- package/dist/usecases/script/insert.js.map +1 -1
- package/dist/usecases/script/lib.d.ts +50 -1
- package/dist/usecases/script/lib.js +599 -61
- package/dist/usecases/script/lib.js.map +1 -1
- package/dist/usecases/script/locations.js +16 -7
- package/dist/usecases/script/locations.js.map +1 -1
- package/dist/usecases/script/merge.js +8 -5
- package/dist/usecases/script/merge.js.map +1 -1
- package/dist/usecases/script/move.js +24 -5
- package/dist/usecases/script/move.js.map +1 -1
- package/dist/usecases/script/parenthetical.d.ts +3 -0
- package/dist/usecases/script/parenthetical.js +55 -0
- package/dist/usecases/script/parenthetical.js.map +1 -0
- package/dist/usecases/script/props.js +16 -7
- package/dist/usecases/script/props.js.map +1 -1
- package/dist/usecases/script/refs.js +44 -0
- package/dist/usecases/script/refs.js.map +1 -1
- package/dist/usecases/script/rename.js +7 -4
- package/dist/usecases/script/rename.js.map +1 -1
- package/dist/usecases/script/scenes.js +32 -6
- package/dist/usecases/script/scenes.js.map +1 -1
- package/dist/usecases/script/script-patch.js +180 -16
- package/dist/usecases/script/script-patch.js.map +1 -1
- package/dist/usecases/script/session.d.ts +3 -0
- package/dist/usecases/script/session.js +25 -5
- package/dist/usecases/script/session.js.map +1 -1
- package/dist/usecases/script/split.js +12 -6
- package/dist/usecases/script/split.js.map +1 -1
- package/dist/usecases/script/states.js +45 -1
- package/dist/usecases/script/states.js.map +1 -1
- package/dist/usecases/script/synopsis.js +11 -4
- package/dist/usecases/script/synopsis.js.map +1 -1
- package/dist/usecases/script/v3/mutations.d.ts +2 -0
- package/dist/usecases/script/v3/mutations.js +5 -0
- package/dist/usecases/script/v3/mutations.js.map +1 -0
- package/dist/usecases/script/v4/mutations.d.ts +2 -0
- package/dist/usecases/script/v4/mutations.js +6 -0
- package/dist/usecases/script/v4/mutations.js.map +1 -0
- package/dist/usecases/script/v4/queries.d.ts +14 -0
- package/dist/usecases/script/v4/queries.js +154 -0
- package/dist/usecases/script/v4/queries.js.map +1 -0
- package/dist/usecases/script/working-copy.js +3 -3
- package/dist/usecases/script/working-copy.js.map +1 -1
- package/dist/usecases/script/write-authoring.js +99 -5
- package/dist/usecases/script/write-authoring.js.map +1 -1
- package/package.json +3 -2
- package/skills/scriptctl/SKILL.md +122 -282
- package/skills/scriptctl/references/atomic-edit-workflow.md +23 -38
- package/skills/scriptctl/references/bulk-authoring.md +35 -154
- package/skills/scriptctl/references/ingest-workflow.md +12 -11
- package/skills/scriptctl/references/production-writing-standard.md +38 -67
- package/skills/scriptctl/references/state-reference-repair.md +19 -42
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import { strOf } from "../../shared.js";
|
|
2
|
+
import { IMPORTANCE_TIER_SET, ROLE_TYPE_SET, } from "../../shared/types.js";
|
|
3
|
+
import { generateV4Id, isV4Id } from "../ids.js";
|
|
4
|
+
import { sortDeep } from "../../text-util.js";
|
|
5
|
+
import { opUsage, requireAsset, requireState, stringField } from "./helpers.js";
|
|
6
|
+
function aliasesFrom(raw) {
|
|
7
|
+
if (raw === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!Array.isArray(raw) || !raw.every((value) => typeof value === "string")) {
|
|
10
|
+
return opUsage("ASSET BLOCKED: aliases invalid", "aliases must be an array of strings.", {
|
|
11
|
+
code: "ALIASES_INVALID",
|
|
12
|
+
received: [JSON.stringify(raw)],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (raw.some((value) => value === "") || new Set(raw).size !== raw.length) {
|
|
16
|
+
return opUsage("ASSET BLOCKED: aliases invalid", "aliases must be non-empty and unique.", {
|
|
17
|
+
code: "ALIASES_INVALID",
|
|
18
|
+
received: raw,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return raw;
|
|
22
|
+
}
|
|
23
|
+
function importanceFrom(raw) {
|
|
24
|
+
const value = strOf(raw).trim() || "featured";
|
|
25
|
+
if (!IMPORTANCE_TIER_SET.has(value)) {
|
|
26
|
+
return opUsage("ASSET BLOCKED: importance invalid", "importance must be featured or background.", {
|
|
27
|
+
code: "IMPORTANCE_INVALID",
|
|
28
|
+
received: [value],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function requestedId(ctx, kind, key, existing) {
|
|
34
|
+
const raw = strOf(ctx.op[key] ?? ctx.op.id).trim();
|
|
35
|
+
if (!raw)
|
|
36
|
+
return generateV4Id(kind, existing);
|
|
37
|
+
if (!isV4Id(kind, raw)) {
|
|
38
|
+
return opUsage("ASSET BLOCKED: ID invalid", `New ${kind} ID has the wrong format.`, {
|
|
39
|
+
code: "V4_ID_INVALID",
|
|
40
|
+
received: [raw],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (existing.has(raw)) {
|
|
44
|
+
return opUsage("ASSET BLOCKED: ID exists", `New ${kind} ID already exists.`, {
|
|
45
|
+
code: "ASSET_ID_EXISTS",
|
|
46
|
+
received: [raw],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
function defaultState() {
|
|
52
|
+
return { state_id: "default", state_name: "default", description: "" };
|
|
53
|
+
}
|
|
54
|
+
export const op_actor_add = (ctx) => {
|
|
55
|
+
const name = strOf(ctx.op.name ?? ctx.op.actor_name).trim();
|
|
56
|
+
if (!name) {
|
|
57
|
+
return opUsage("ASSET BLOCKED: actor name empty", "Actor name is required.", {
|
|
58
|
+
code: "ASSET_NAME_EMPTY",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (ctx.script.actors.some((actor) => actor.actor_name.trim().toLocaleLowerCase() === name.toLocaleLowerCase())) {
|
|
62
|
+
return opUsage("ASSET BLOCKED: actor name exists", "Actor names must be unique.", {
|
|
63
|
+
code: "ACTOR_NAME_EXISTS",
|
|
64
|
+
received: [name],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const existing = new Set(ctx.script.actors.map((actor) => actor.actor_id));
|
|
68
|
+
const actorId = requestedId(ctx, "actor", "actor_id", existing);
|
|
69
|
+
const role = strOf(ctx.op.role_type ?? ctx.op.role).trim();
|
|
70
|
+
if (role && !ROLE_TYPE_SET.has(role)) {
|
|
71
|
+
return opUsage("ASSET BLOCKED: role invalid", "role_type must be 主角 or 配角.", {
|
|
72
|
+
code: "ROLE_TYPE_INVALID",
|
|
73
|
+
received: [role],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const aliases = aliasesFrom(ctx.op.aliases);
|
|
77
|
+
if (aliases?.includes(name)) {
|
|
78
|
+
return opUsage("ASSET BLOCKED: alias equals name", "An alias must differ from the asset name.", {
|
|
79
|
+
code: "ALIAS_EQUALS_NAME",
|
|
80
|
+
received: [name],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
ctx.script.actors.push({
|
|
84
|
+
actor_id: actorId,
|
|
85
|
+
actor_name: name,
|
|
86
|
+
importance: importanceFrom(ctx.op.importance),
|
|
87
|
+
description: strOf(ctx.op.description).trim(),
|
|
88
|
+
states: [defaultState()],
|
|
89
|
+
...(role ? { role_type: role } : {}),
|
|
90
|
+
...(aliases && aliases.length > 0 ? { aliases } : {}),
|
|
91
|
+
});
|
|
92
|
+
ctx.applied.push(ctx.kind);
|
|
93
|
+
};
|
|
94
|
+
export const op_location_add = (ctx) => {
|
|
95
|
+
const name = strOf(ctx.op.name ?? ctx.op.location_name).trim();
|
|
96
|
+
if (!name) {
|
|
97
|
+
return opUsage("ASSET BLOCKED: location name empty", "Location name is required.", {
|
|
98
|
+
code: "ASSET_NAME_EMPTY",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const existing = new Set(ctx.script.locations.map((asset) => asset.location_id));
|
|
102
|
+
ctx.script.locations.push({
|
|
103
|
+
location_id: requestedId(ctx, "location", "location_id", existing),
|
|
104
|
+
location_name: name,
|
|
105
|
+
importance: importanceFrom(ctx.op.importance),
|
|
106
|
+
description: strOf(ctx.op.description).trim(),
|
|
107
|
+
states: [defaultState()],
|
|
108
|
+
});
|
|
109
|
+
ctx.applied.push(ctx.kind);
|
|
110
|
+
};
|
|
111
|
+
export const op_prop_add = (ctx) => {
|
|
112
|
+
const name = strOf(ctx.op.name ?? ctx.op.prop_name).trim();
|
|
113
|
+
if (!name) {
|
|
114
|
+
return opUsage("ASSET BLOCKED: prop name empty", "Prop name is required.", {
|
|
115
|
+
code: "ASSET_NAME_EMPTY",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const existing = new Set(ctx.script.props.map((asset) => asset.prop_id));
|
|
119
|
+
ctx.script.props.push({
|
|
120
|
+
prop_id: requestedId(ctx, "prop", "prop_id", existing),
|
|
121
|
+
prop_name: name,
|
|
122
|
+
importance: importanceFrom(ctx.op.importance),
|
|
123
|
+
description: strOf(ctx.op.description).trim(),
|
|
124
|
+
states: [defaultState()],
|
|
125
|
+
});
|
|
126
|
+
ctx.applied.push(ctx.kind);
|
|
127
|
+
};
|
|
128
|
+
export const op_state_add = (ctx) => {
|
|
129
|
+
const target = requireAsset(ctx.script, ctx.op.target);
|
|
130
|
+
const name = strOf(ctx.op.name ?? ctx.op.state_name).trim();
|
|
131
|
+
if (!name) {
|
|
132
|
+
return opUsage("STATE BLOCKED: name empty", "State name is required.", {
|
|
133
|
+
code: "STATE_NAME_EMPTY",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (target.asset.states.some((state) => state.state_name === name)) {
|
|
137
|
+
return opUsage("STATE BLOCKED: name exists", "State name already exists on this asset.", {
|
|
138
|
+
code: "STATE_NAME_EXISTS",
|
|
139
|
+
received: [name],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const existing = new Set(target.asset.states.map((state) => state.state_id));
|
|
143
|
+
const raw = strOf(ctx.op.state_id ?? ctx.op.id).trim();
|
|
144
|
+
const stateId = raw || generateV4Id("state", existing);
|
|
145
|
+
if (stateId === "default" || !isV4Id("state", stateId)) {
|
|
146
|
+
return opUsage("STATE BLOCKED: ID invalid", "New state ID must use the v4 random state format.", {
|
|
147
|
+
code: "STATE_ID_INVALID",
|
|
148
|
+
received: [stateId],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (existing.has(stateId)) {
|
|
152
|
+
return opUsage("STATE BLOCKED: ID exists", "State ID already exists on this asset.", {
|
|
153
|
+
code: "STATE_ID_EXISTS",
|
|
154
|
+
received: [stateId],
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
target.asset.states.push({
|
|
158
|
+
state_id: stateId,
|
|
159
|
+
state_name: name,
|
|
160
|
+
description: strOf(ctx.op.description).trim(),
|
|
161
|
+
});
|
|
162
|
+
ctx.applied.push(ctx.kind);
|
|
163
|
+
};
|
|
164
|
+
export const op_asset_rename = (ctx) => {
|
|
165
|
+
const target = requireAsset(ctx.script, ctx.op.target);
|
|
166
|
+
const name = stringField(ctx.op, "name").trim();
|
|
167
|
+
if (!name) {
|
|
168
|
+
return opUsage("ASSET BLOCKED: name empty", "Name must not be empty.", {
|
|
169
|
+
code: "ASSET_NAME_EMPTY",
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (target.asset.aliases?.includes(name)) {
|
|
173
|
+
return opUsage("ASSET BLOCKED: name equals alias", "The new name must differ from every alias.", {
|
|
174
|
+
code: "ASSET_NAME_EQUALS_ALIAS",
|
|
175
|
+
received: [name],
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (target.kind === "actor"
|
|
179
|
+
&& ctx.script.actors.some((actor) => actor.actor_id !== target.id
|
|
180
|
+
&& actor.actor_name.trim().toLocaleLowerCase() === name.toLocaleLowerCase())) {
|
|
181
|
+
return opUsage("ASSET BLOCKED: actor name exists", "Actor names must be unique.", {
|
|
182
|
+
code: "ACTOR_NAME_EXISTS",
|
|
183
|
+
received: [name],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (target.kind === "actor")
|
|
187
|
+
target.asset.actor_name = name;
|
|
188
|
+
else if (target.kind === "location")
|
|
189
|
+
target.asset.location_name = name;
|
|
190
|
+
else
|
|
191
|
+
target.asset.prop_name = name;
|
|
192
|
+
ctx.applied.push(ctx.kind);
|
|
193
|
+
};
|
|
194
|
+
export const op_asset_describe = (ctx) => {
|
|
195
|
+
requireAsset(ctx.script, ctx.op.target).asset.description = stringField(ctx.op, "description");
|
|
196
|
+
ctx.applied.push(ctx.kind);
|
|
197
|
+
};
|
|
198
|
+
export const op_importance_set = (ctx) => {
|
|
199
|
+
requireAsset(ctx.script, ctx.op.target).asset.importance = importanceFrom(ctx.op.importance);
|
|
200
|
+
ctx.applied.push(ctx.kind);
|
|
201
|
+
};
|
|
202
|
+
export const op_asset_role_set = (ctx) => {
|
|
203
|
+
const target = requireAsset(ctx.script, ctx.op.target);
|
|
204
|
+
if (target.kind !== "actor") {
|
|
205
|
+
return opUsage("ROLE BLOCKED: target is not an actor", "Role only belongs to actor assets.", {
|
|
206
|
+
code: "ROLE_TARGET_INVALID",
|
|
207
|
+
received: [target.id],
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const role = strOf(ctx.op.role_type ?? ctx.op.role).trim();
|
|
211
|
+
if (!ROLE_TYPE_SET.has(role)) {
|
|
212
|
+
return opUsage("ROLE BLOCKED: role invalid", "role_type must be 主角 or 配角.", {
|
|
213
|
+
code: "ROLE_TYPE_INVALID",
|
|
214
|
+
received: [role || "<empty>"],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
target.asset.role_type = role;
|
|
218
|
+
ctx.applied.push(ctx.kind);
|
|
219
|
+
};
|
|
220
|
+
function aliasesFromOp(ctx) {
|
|
221
|
+
const raw = ctx.op.alias ?? ctx.op.aliases;
|
|
222
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
223
|
+
if (!values.every((value) => typeof value === "string" && value.trim() !== "")) {
|
|
224
|
+
return opUsage("ALIAS BLOCKED: alias invalid", "Aliases must be non-empty strings.", {
|
|
225
|
+
code: "ALIAS_INVALID",
|
|
226
|
+
received: [JSON.stringify(raw)],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const aliases = values.map((value) => value.trim());
|
|
230
|
+
if (new Set(aliases).size !== aliases.length) {
|
|
231
|
+
return opUsage("ALIAS BLOCKED: alias duplicated", "Aliases in one operation must be unique.", {
|
|
232
|
+
code: "ALIAS_DUPLICATE",
|
|
233
|
+
received: aliases,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return aliases;
|
|
237
|
+
}
|
|
238
|
+
function assetName(asset) {
|
|
239
|
+
if ("actor_name" in asset)
|
|
240
|
+
return asset.actor_name;
|
|
241
|
+
if ("location_name" in asset)
|
|
242
|
+
return asset.location_name;
|
|
243
|
+
return asset.prop_name;
|
|
244
|
+
}
|
|
245
|
+
function assertAliasesDifferFromName(target, aliases) {
|
|
246
|
+
const name = assetName(target);
|
|
247
|
+
if (aliases.includes(name)) {
|
|
248
|
+
opUsage("ALIAS BLOCKED: alias equals name", "An alias must differ from the asset name.", {
|
|
249
|
+
code: "ALIAS_EQUALS_NAME",
|
|
250
|
+
received: [name],
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
export const op_asset_alias_set = (ctx) => {
|
|
255
|
+
const target = requireAsset(ctx.script, ctx.op.target).asset;
|
|
256
|
+
const aliases = aliasesFromOp(ctx);
|
|
257
|
+
assertAliasesDifferFromName(target, aliases);
|
|
258
|
+
target.aliases = aliases;
|
|
259
|
+
if (target.aliases.length === 0)
|
|
260
|
+
delete target.aliases;
|
|
261
|
+
ctx.applied.push(ctx.kind);
|
|
262
|
+
};
|
|
263
|
+
export const op_asset_alias_add = (ctx) => {
|
|
264
|
+
const target = requireAsset(ctx.script, ctx.op.target).asset;
|
|
265
|
+
const added = aliasesFromOp(ctx);
|
|
266
|
+
assertAliasesDifferFromName(target, added);
|
|
267
|
+
const duplicates = added.filter((alias) => target.aliases?.includes(alias));
|
|
268
|
+
if (duplicates.length > 0) {
|
|
269
|
+
return opUsage("ALIAS BLOCKED: alias exists", "The asset already has this alias.", {
|
|
270
|
+
code: "ALIAS_EXISTS",
|
|
271
|
+
received: duplicates,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const aliases = [...(target.aliases ?? []), ...added];
|
|
275
|
+
target.aliases = aliases;
|
|
276
|
+
ctx.applied.push(ctx.kind);
|
|
277
|
+
};
|
|
278
|
+
export const op_asset_alias_remove = (ctx) => {
|
|
279
|
+
const target = requireAsset(ctx.script, ctx.op.target).asset;
|
|
280
|
+
const removing = new Set(aliasesFromOp(ctx));
|
|
281
|
+
const aliases = (target.aliases ?? []).filter((alias) => !removing.has(alias));
|
|
282
|
+
if (aliases.length > 0)
|
|
283
|
+
target.aliases = aliases;
|
|
284
|
+
else
|
|
285
|
+
delete target.aliases;
|
|
286
|
+
ctx.applied.push(ctx.kind);
|
|
287
|
+
};
|
|
288
|
+
export const op_state_rename = (ctx) => {
|
|
289
|
+
const target = requireState(ctx.script, ctx.op.target);
|
|
290
|
+
const name = stringField(ctx.op, "name").trim();
|
|
291
|
+
if (!name) {
|
|
292
|
+
return opUsage("STATE BLOCKED: name empty", "State name must not be empty.", {
|
|
293
|
+
code: "STATE_NAME_EMPTY",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (target.owner.asset.states.some((state) => state !== target.state && state.state_name === name)) {
|
|
297
|
+
return opUsage("STATE BLOCKED: name exists", "State name already exists on this asset.", {
|
|
298
|
+
code: "STATE_NAME_EXISTS",
|
|
299
|
+
received: [name],
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
target.state.state_name = name;
|
|
303
|
+
ctx.applied.push(ctx.kind);
|
|
304
|
+
};
|
|
305
|
+
export const op_state_describe = (ctx) => {
|
|
306
|
+
requireState(ctx.script, ctx.op.target).state.description = stringField(ctx.op, "description");
|
|
307
|
+
ctx.applied.push(ctx.kind);
|
|
308
|
+
};
|
|
309
|
+
function replaceStateId(ids, from, replacement) {
|
|
310
|
+
const next = ids.flatMap((id) => id === from ? (replacement ? [replacement] : []) : [id]);
|
|
311
|
+
return [...new Set(next)].sort();
|
|
312
|
+
}
|
|
313
|
+
export const op_state_delete = (ctx) => {
|
|
314
|
+
const target = requireState(ctx.script, ctx.op.target);
|
|
315
|
+
if (target.state.state_id === "default") {
|
|
316
|
+
return opUsage("STATE DELETE BLOCKED: default is protected", "Every asset must retain its default state.", {
|
|
317
|
+
code: "STATE_DEFAULT_PROTECTED",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
const strategy = strOf(ctx.op.strategy).trim();
|
|
321
|
+
if (strategy !== "replace" && strategy !== "remove") {
|
|
322
|
+
return opUsage("STATE DELETE BLOCKED: strategy invalid", "Choose replace or remove.", {
|
|
323
|
+
code: "DELETE_STRATEGY_INVALID",
|
|
324
|
+
received: [strategy || "<empty>"],
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (strategy === "remove" && ctx.op.replacement !== undefined) {
|
|
328
|
+
return opUsage("STATE DELETE BLOCKED: replacement conflicts with remove", "replacement only belongs to the replace strategy.", {
|
|
329
|
+
code: "DELETE_REPLACEMENT_UNEXPECTED",
|
|
330
|
+
received: [strOf(ctx.op.replacement)],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const replacementRaw = strOf(ctx.op.replacement).trim();
|
|
334
|
+
const replacement = strategy === "replace"
|
|
335
|
+
? target.owner.asset.states.find((state) => state.state_id === replacementRaw && state.state_id !== target.state.state_id)
|
|
336
|
+
: undefined;
|
|
337
|
+
if (strategy === "replace" && !replacement) {
|
|
338
|
+
return opUsage("STATE DELETE BLOCKED: replacement invalid", "Replacement must be another state on the same asset.", {
|
|
339
|
+
code: "REPLACEMENT_INVALID",
|
|
340
|
+
received: [replacementRaw || "<empty>"],
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const fromId = target.state.state_id;
|
|
344
|
+
const replacementId = replacement?.state_id ?? null;
|
|
345
|
+
const stateRefUpdates = [];
|
|
346
|
+
const queueStateRefUpdate = (sceneId, stateIds, write) => {
|
|
347
|
+
const next = replaceStateId(stateIds, fromId, replacementId);
|
|
348
|
+
if (next.length === 0) {
|
|
349
|
+
return opUsage("STATE DELETE BLOCKED: scene state set would be empty", "Replace the state or add another state to the scene first.", {
|
|
350
|
+
code: "SCENE_REF_STATES_EMPTY",
|
|
351
|
+
received: [sceneId, fromId],
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
stateRefUpdates.push(() => write(next));
|
|
355
|
+
};
|
|
356
|
+
for (const episode of ctx.script.episodes) {
|
|
357
|
+
for (const scene of episode.scenes) {
|
|
358
|
+
if (target.owner.kind === "location" && scene.location?.location_id === target.owner.id) {
|
|
359
|
+
const ref = scene.location;
|
|
360
|
+
queueStateRefUpdate(scene.scene_id, ref.state_ids, (next) => {
|
|
361
|
+
ref.state_ids = next;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else if (target.owner.kind === "actor") {
|
|
365
|
+
const ref = scene.actors.find((candidate) => candidate.actor_id === target.owner.id);
|
|
366
|
+
if (ref) {
|
|
367
|
+
queueStateRefUpdate(scene.scene_id, ref.state_ids, (next) => {
|
|
368
|
+
ref.state_ids = next;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else if (target.owner.kind === "prop") {
|
|
373
|
+
const ref = scene.props.find((candidate) => candidate.prop_id === target.owner.id);
|
|
374
|
+
if (ref) {
|
|
375
|
+
queueStateRefUpdate(scene.scene_id, ref.state_ids, (next) => {
|
|
376
|
+
ref.state_ids = next;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const update of stateRefUpdates)
|
|
383
|
+
update();
|
|
384
|
+
if (target.owner.kind === "actor") {
|
|
385
|
+
for (const episode of ctx.script.episodes) {
|
|
386
|
+
for (const scene of episode.scenes) {
|
|
387
|
+
for (const action of scene.actions) {
|
|
388
|
+
if ((action.type === "dialogue" || action.type === "inner_thought")
|
|
389
|
+
&& "actor_id" in action.speaker
|
|
390
|
+
&& action.speaker.actor_id === target.owner.id
|
|
391
|
+
&& action.speaker.state_id === fromId) {
|
|
392
|
+
if (replacementId)
|
|
393
|
+
action.speaker.state_id = replacementId;
|
|
394
|
+
else
|
|
395
|
+
delete action.speaker.state_id;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
target.owner.asset.states.splice(target.stateIndex, 1);
|
|
402
|
+
ctx.applied.push(ctx.kind);
|
|
403
|
+
};
|
|
404
|
+
function assetRefCount(ctx, kind, id) {
|
|
405
|
+
let count = 0;
|
|
406
|
+
for (const episode of ctx.script.episodes) {
|
|
407
|
+
for (const scene of episode.scenes) {
|
|
408
|
+
if (kind === "actor") {
|
|
409
|
+
if (scene.actors.some((ref) => ref.actor_id === id))
|
|
410
|
+
count++;
|
|
411
|
+
count += scene.actions.filter((action) => (action.type === "dialogue" || action.type === "inner_thought")
|
|
412
|
+
&& "actor_id" in action.speaker
|
|
413
|
+
&& action.speaker.actor_id === id).length;
|
|
414
|
+
}
|
|
415
|
+
else if (kind === "location" && scene.location?.location_id === id) {
|
|
416
|
+
count++;
|
|
417
|
+
}
|
|
418
|
+
else if (kind === "prop" && scene.props.some((ref) => ref.prop_id === id)) {
|
|
419
|
+
count++;
|
|
420
|
+
}
|
|
421
|
+
count += scene.actions.filter((action) => action.transition_prompt?.target === id).length;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return count;
|
|
425
|
+
}
|
|
426
|
+
export const op_asset_merge = (ctx) => {
|
|
427
|
+
const from = requireAsset(ctx.script, ctx.op.from ?? ctx.op.source);
|
|
428
|
+
const into = requireAsset(ctx.script, ctx.op.into ?? ctx.op.target);
|
|
429
|
+
if (from.kind !== into.kind) {
|
|
430
|
+
return opUsage("ASSET MERGE BLOCKED: kind mismatch", "Both assets must have the same kind.", {
|
|
431
|
+
code: "MERGE_KIND_MISMATCH",
|
|
432
|
+
received: [from.kind, into.kind],
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (from.id === into.id) {
|
|
436
|
+
return opUsage("ASSET MERGE BLOCKED: same asset", "An asset cannot be merged into itself.", {
|
|
437
|
+
code: "MERGE_SELF",
|
|
438
|
+
received: [from.id],
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
const fromStates = new Map(from.asset.states.map((state) => [state.state_id, state]));
|
|
442
|
+
const intoStates = new Map(into.asset.states.map((state) => [state.state_id, state]));
|
|
443
|
+
const referencedStates = new Set();
|
|
444
|
+
for (const episode of ctx.script.episodes) {
|
|
445
|
+
for (const scene of episode.scenes) {
|
|
446
|
+
if (from.kind === "location" && scene.location?.location_id === from.id) {
|
|
447
|
+
scene.location.state_ids.forEach((id) => referencedStates.add(id));
|
|
448
|
+
}
|
|
449
|
+
else if (from.kind === "actor") {
|
|
450
|
+
scene.actors.find((ref) => ref.actor_id === from.id)?.state_ids.forEach((id) => referencedStates.add(id));
|
|
451
|
+
}
|
|
452
|
+
else if (from.kind === "prop") {
|
|
453
|
+
scene.props.find((ref) => ref.prop_id === from.id)?.state_ids.forEach((id) => referencedStates.add(id));
|
|
454
|
+
}
|
|
455
|
+
if (from.kind === "actor") {
|
|
456
|
+
for (const action of scene.actions) {
|
|
457
|
+
if ((action.type === "dialogue" || action.type === "inner_thought")
|
|
458
|
+
&& "actor_id" in action.speaker
|
|
459
|
+
&& action.speaker.actor_id === from.id
|
|
460
|
+
&& action.speaker.state_id)
|
|
461
|
+
referencedStates.add(action.speaker.state_id);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const missing = [...referencedStates].filter((id) => !intoStates.has(id));
|
|
467
|
+
const conflicting = [...referencedStates].filter((id) => {
|
|
468
|
+
if (id === "default")
|
|
469
|
+
return false;
|
|
470
|
+
const sourceState = fromStates.get(id);
|
|
471
|
+
const survivorState = intoStates.get(id);
|
|
472
|
+
return sourceState !== undefined
|
|
473
|
+
&& survivorState !== undefined
|
|
474
|
+
&& JSON.stringify(sortDeep(sourceState)) !== JSON.stringify(sortDeep(survivorState));
|
|
475
|
+
});
|
|
476
|
+
if (missing.length > 0 || conflicting.length > 0) {
|
|
477
|
+
return opUsage("ASSET MERGE BLOCKED: state mismatch", "The survivor lacks a referenced source state or uses the same owner-local ID for a different state.", {
|
|
478
|
+
code: "MERGE_STATE_CONFLICT",
|
|
479
|
+
received: [
|
|
480
|
+
...missing.map((id) => `missing:${id}`),
|
|
481
|
+
...conflicting.map((id) => `different:${id}`),
|
|
482
|
+
],
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
for (const episode of ctx.script.episodes) {
|
|
486
|
+
for (const scene of episode.scenes) {
|
|
487
|
+
if (from.kind === "location" && into.kind === "location" && scene.location?.location_id === from.id) {
|
|
488
|
+
scene.location.location_id = into.id;
|
|
489
|
+
}
|
|
490
|
+
else if (from.kind === "actor" && into.kind === "actor") {
|
|
491
|
+
const sourceIndex = scene.actors.findIndex((ref) => ref.actor_id === from.id);
|
|
492
|
+
if (sourceIndex >= 0) {
|
|
493
|
+
const sourceRef = scene.actors[sourceIndex];
|
|
494
|
+
const targetRef = scene.actors.find((ref) => ref.actor_id === into.id);
|
|
495
|
+
if (targetRef) {
|
|
496
|
+
targetRef.state_ids = [...new Set([...targetRef.state_ids, ...sourceRef.state_ids])].sort();
|
|
497
|
+
scene.actors.splice(sourceIndex, 1);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
sourceRef.actor_id = into.id;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
else if (from.kind === "prop" && into.kind === "prop") {
|
|
505
|
+
const sourceIndex = scene.props.findIndex((ref) => ref.prop_id === from.id);
|
|
506
|
+
if (sourceIndex >= 0) {
|
|
507
|
+
const sourceRef = scene.props[sourceIndex];
|
|
508
|
+
const targetRef = scene.props.find((ref) => ref.prop_id === into.id);
|
|
509
|
+
if (targetRef) {
|
|
510
|
+
targetRef.state_ids = [...new Set([...targetRef.state_ids, ...sourceRef.state_ids])].sort();
|
|
511
|
+
scene.props.splice(sourceIndex, 1);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
sourceRef.prop_id = into.id;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
for (const action of scene.actions) {
|
|
519
|
+
if (from.kind === "actor"
|
|
520
|
+
&& into.kind === "actor"
|
|
521
|
+
&& (action.type === "dialogue" || action.type === "inner_thought")
|
|
522
|
+
&& "actor_id" in action.speaker
|
|
523
|
+
&& action.speaker.actor_id === from.id)
|
|
524
|
+
action.speaker.actor_id = into.id;
|
|
525
|
+
if (action.transition_prompt?.target === from.id)
|
|
526
|
+
action.transition_prompt.target = into.id;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const survivorName = assetName(into.asset);
|
|
531
|
+
const mergedAliases = [...new Set([
|
|
532
|
+
...(into.asset.aliases ?? []),
|
|
533
|
+
assetName(from.asset),
|
|
534
|
+
...(from.asset.aliases ?? []),
|
|
535
|
+
])].filter((alias) => alias !== survivorName);
|
|
536
|
+
assertAliasesDifferFromName(into.asset, mergedAliases);
|
|
537
|
+
into.asset.aliases = mergedAliases;
|
|
538
|
+
if (into.asset.aliases.length === 0)
|
|
539
|
+
delete into.asset.aliases;
|
|
540
|
+
if (from.asset.extend)
|
|
541
|
+
into.asset.extend = { ...from.asset.extend, ...(into.asset.extend ?? {}) };
|
|
542
|
+
if (from.kind === "actor")
|
|
543
|
+
ctx.script.actors = ctx.script.actors.filter((asset) => asset.actor_id !== from.id);
|
|
544
|
+
else if (from.kind === "location")
|
|
545
|
+
ctx.script.locations = ctx.script.locations.filter((asset) => asset.location_id !== from.id);
|
|
546
|
+
else
|
|
547
|
+
ctx.script.props = ctx.script.props.filter((asset) => asset.prop_id !== from.id);
|
|
548
|
+
ctx.applied.push(ctx.kind);
|
|
549
|
+
};
|
|
550
|
+
export const op_asset_delete = (ctx) => {
|
|
551
|
+
const target = requireAsset(ctx.script, ctx.op.target);
|
|
552
|
+
const count = assetRefCount(ctx, target.kind, target.id);
|
|
553
|
+
const strategy = strOf(ctx.op.strategy).trim();
|
|
554
|
+
if (strategy !== "" && strategy !== "replace" && strategy !== "remove") {
|
|
555
|
+
return opUsage("ASSET DELETE BLOCKED: strategy invalid", "Choose replace or remove.", {
|
|
556
|
+
code: "DELETE_STRATEGY_INVALID",
|
|
557
|
+
received: [strategy],
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
if (strategy === "replace") {
|
|
561
|
+
const replacement = strOf(ctx.op.replacement).trim();
|
|
562
|
+
if (!replacement) {
|
|
563
|
+
return opUsage("ASSET DELETE BLOCKED: replacement missing", "The replace strategy requires a replacement asset.", {
|
|
564
|
+
code: "REPLACEMENT_INVALID",
|
|
565
|
+
received: ["<empty>"],
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
ctx.runOps([{ op: "asset.merge", from: target.id, into: replacement }]);
|
|
569
|
+
ctx.applied.push(ctx.kind);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (ctx.op.replacement !== undefined) {
|
|
573
|
+
return opUsage("ASSET DELETE BLOCKED: replacement conflicts with remove", "replacement only belongs to the replace strategy.", {
|
|
574
|
+
code: "DELETE_REPLACEMENT_UNEXPECTED",
|
|
575
|
+
received: [strOf(ctx.op.replacement)],
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
if (count > 0 && !strategy) {
|
|
579
|
+
return opUsage("ASSET DELETE BLOCKED: asset is referenced", "Choose replace or remove.", {
|
|
580
|
+
code: "ASSET_HAS_REFS",
|
|
581
|
+
received: [`refs=${count}`],
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (target.kind === "actor") {
|
|
585
|
+
const speaking = ctx.script.episodes.flatMap((episode) => episode.scenes).some((scene) => scene.actions.some((action) => (action.type === "dialogue" || action.type === "inner_thought")
|
|
586
|
+
&& "actor_id" in action.speaker
|
|
587
|
+
&& action.speaker.actor_id === target.id));
|
|
588
|
+
if (speaking) {
|
|
589
|
+
return opUsage("ASSET DELETE BLOCKED: actor still speaks", "Reassign or delete the actor's speaking actions first.", {
|
|
590
|
+
code: "ACTOR_SPEAKER_IN_USE",
|
|
591
|
+
received: [target.id],
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
for (const episode of ctx.script.episodes) {
|
|
596
|
+
for (const scene of episode.scenes) {
|
|
597
|
+
if (target.kind === "actor")
|
|
598
|
+
scene.actors = scene.actors.filter((ref) => ref.actor_id !== target.id);
|
|
599
|
+
else if (target.kind === "location" && scene.location?.location_id === target.id)
|
|
600
|
+
scene.location = null;
|
|
601
|
+
else if (target.kind === "prop")
|
|
602
|
+
scene.props = scene.props.filter((ref) => ref.prop_id !== target.id);
|
|
603
|
+
for (const action of scene.actions) {
|
|
604
|
+
if (action.transition_prompt?.target === target.id)
|
|
605
|
+
delete action.transition_prompt;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (target.kind === "actor")
|
|
610
|
+
ctx.script.actors = ctx.script.actors.filter((asset) => asset.actor_id !== target.id);
|
|
611
|
+
else if (target.kind === "location")
|
|
612
|
+
ctx.script.locations = ctx.script.locations.filter((asset) => asset.location_id !== target.id);
|
|
613
|
+
else
|
|
614
|
+
ctx.script.props = ctx.script.props.filter((asset) => asset.prop_id !== target.id);
|
|
615
|
+
ctx.applied.push(ctx.kind);
|
|
616
|
+
};
|
|
617
|
+
//# sourceMappingURL=ops-asset.js.map
|