@patterkit/ops 0.8.0 → 0.10.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/dist/index.d.ts CHANGED
@@ -142,10 +142,17 @@ declare function defaultGameScopesDir(root: string): string;
142
142
  * engine's, which is that engine's to declare). The project keeps its copy. When the walk-up from the
143
143
  * project would not find `dir`, the project names it (`gameScopes`), so it is found next time. Returns
144
144
  * the writes (the folder comes with the first), and the project as it should now be saved.
145
+ *
146
+ * A folder another tool already made is JOINED, never overwritten: an existing `game.scopes.json` keeps
147
+ * every scope it holds, and gains only the project's game scopes it doesn't declare yet (as the
148
+ * Storylet Engine's share adds `@world` only when the file has none). One that won't parse is an error,
149
+ * and nothing is written.
145
150
  */
146
151
  declare function planShareScopes(root: string, project: ProjectFile, dir: string): {
147
152
  writes: PlannedWrite[];
148
153
  project: ProjectFile;
154
+ } | {
155
+ error: string;
149
156
  };
150
157
 
151
158
  interface LoadedProject {
@@ -921,6 +928,26 @@ interface ReplacePlan {
921
928
  */
922
929
  declare function runReplace(loaded: LoadedProject, opts: ReplaceOptions): ReplacePlan;
923
930
 
931
+ /** One address written down. */
932
+ interface PinnedName {
933
+ /** The node's immutable id. */
934
+ id: string;
935
+ kind: "scene" | "block";
936
+ /** The scene it belongs to (itself, for a scene). */
937
+ sceneId: string;
938
+ /** The address, now pinned: the same one it had while it followed the name. */
939
+ gameId: string;
940
+ }
941
+ interface PinPlan {
942
+ pinned: PinnedName[];
943
+ /** Flow-shard writes the caller commits through the VC layer (one per touched scene). */
944
+ writes: PlannedWrite[];
945
+ /** The rewritten scenes, index-aligned with `writes`. */
946
+ scenes: Scene[];
947
+ }
948
+ /** Plan pinning every scene and block address still following its name. */
949
+ declare function planPins(loaded: LoadedProject): PinPlan;
950
+
924
951
  /** The source-shard extensions a document carries (the merge-friendly truth). */
925
952
  declare const SHARD_EXTENSIONS: readonly [".patterflow", ".patterloc", ".patterx", ".patterproj"];
926
953
  /** Manifest embedded at the document root - what this envelope is and contains. */
@@ -1087,4 +1114,4 @@ declare function classesForChannel(classes: DocumentationClass[], channel: strin
1087
1114
  */
1088
1115
  declare function resolveDocumentation(loaded: LoadedProject, channel: string): Map<string, DocLine[]>;
1089
1116
 
1090
- export { AUTHORING_HANDLED, type ApplyStats, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageReport, type DocumentManifest, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, type HygieneIssue, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type SearchFocus, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applyWrites, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, extractLoc, findProjectFile, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, hostScopeTokens, jsonToCatalog, listProjectTags, loadProject, loadProjectLanding, patterScopesFile, patterScopesStale, patterScopesWrite, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, proposeCoverageDrivers, reachabilityIssues, readGameScopes, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudioStatus, sceneIdForShard, scriptToDocx, scriptToPdf, sidecarIssues, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
1117
+ export { AUTHORING_HANDLED, type ApplyStats, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageReport, type DocumentManifest, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, type HygieneIssue, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type SearchFocus, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applyWrites, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, extractLoc, findProjectFile, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, hostScopeTokens, jsonToCatalog, listProjectTags, loadProject, loadProjectLanding, patterScopesFile, patterScopesStale, patterScopesWrite, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, proposeCoverageDrivers, reachabilityIssues, readGameScopes, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudioStatus, sceneIdForShard, scriptToDocx, scriptToPdf, sidecarIssues, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
package/dist/index.js CHANGED
@@ -211,18 +211,23 @@ function defaultGameScopesDir(root) {
211
211
  }
212
212
  function planShareScopes(root, project, dir) {
213
213
  const target = resolve(dir);
214
- const game = {
215
- version: 1,
216
- owner: GAME_SCOPES_OWNER,
217
- scopes: (project.scopeRegistry?.scopes ?? []).filter((h) => isGameToken(h.token, void 0)).map((h) => ({
218
- token: h.token,
219
- ...h.writable !== void 0 ? { writable: h.writable } : {},
220
- ...h.declarations ? { declarations: h.declarations.map((d) => ({ ...d })) } : {}
221
- }))
222
- };
214
+ const gamePath = join(target, GAME_SCOPES_FILE);
215
+ let existing = { version: 1, owner: GAME_SCOPES_OWNER, scopes: [] };
216
+ if (existsSync(gamePath)) {
217
+ const parsed = parseScopesFile(readFileSync(gamePath, "utf8"), GAME_SCOPES_FILE);
218
+ if (!parsed.file) return { error: `${GAME_SCOPES_FILE} in ${target} won't parse, so it was left alone: ${parsed.issues.map((i) => i.message).join("; ")}` };
219
+ existing = parsed.file;
220
+ }
221
+ const held = new Set(existing.scopes.map((s) => s.token));
222
+ const added = (project.scopeRegistry?.scopes ?? []).filter((h) => isGameToken(h.token, void 0) && !held.has(h.token)).map((h) => ({
223
+ token: h.token,
224
+ ...h.writable !== void 0 ? { writable: h.writable } : {},
225
+ ...h.declarations ? { declarations: h.declarations.map((d) => ({ ...d })) } : {}
226
+ }));
227
+ const game = { ...existing, scopes: [...existing.scopes, ...added] };
223
228
  const writes = [
224
229
  { path: join(target, PATTER_SCOPES_FILE), content: serialiseScopesFile(patterScopesFile(project)) },
225
- { path: join(target, GAME_SCOPES_FILE), content: serialiseScopesFile(game) }
230
+ { path: gamePath, content: serialiseScopesFile(game) }
226
231
  ];
227
232
  let found = false;
228
233
  for (let at = resolve(root); ; ) {
@@ -3872,18 +3877,54 @@ function runReplace(loaded, opts) {
3872
3877
  return plan;
3873
3878
  }
3874
3879
 
3880
+ // src/pin.ts
3881
+ import { readFileSync as readFileSync5 } from "fs";
3882
+ import { canonicalStringify as canonicalStringify8, parseSource as parseSource4 } from "@patterkit/core";
3883
+ import { effectiveGameId as effectiveGameId2, gameIdify } from "@patterkit/model";
3884
+ function toPin(node) {
3885
+ if (node.gameId?.trim()) return void 0;
3886
+ if (!gameIdify(node.name)) return void 0;
3887
+ return effectiveGameId2(node);
3888
+ }
3889
+ function planPins(loaded) {
3890
+ const plan = { pinned: [], writes: [], scenes: [] };
3891
+ for (const scene of loaded.scenes) {
3892
+ const path = loaded.sceneFiles[scene.id];
3893
+ if (!path) continue;
3894
+ let changed = false;
3895
+ const sceneGameId = toPin(scene);
3896
+ if (sceneGameId !== void 0) {
3897
+ plan.pinned.push({ id: scene.id, kind: "scene", sceneId: scene.id, gameId: sceneGameId });
3898
+ changed = true;
3899
+ }
3900
+ const blocks = scene.blocks.map((block) => {
3901
+ const gameId = toPin(block);
3902
+ if (gameId === void 0) return block;
3903
+ plan.pinned.push({ id: block.id, kind: "block", sceneId: scene.id, gameId });
3904
+ changed = true;
3905
+ return { ...block, gameId };
3906
+ });
3907
+ if (!changed) continue;
3908
+ const next = { ...scene, ...sceneGameId !== void 0 ? { gameId: sceneGameId } : {}, blocks };
3909
+ const file = parseSource4(readFileSync5(path, "utf8"));
3910
+ plan.writes.push({ path, content: canonicalStringify8({ ...file, scene: next }) });
3911
+ plan.scenes.push(next);
3912
+ }
3913
+ return plan;
3914
+ }
3915
+
3875
3916
  // src/pack.ts
3876
3917
  import JSZip from "jszip";
3877
- import { readFileSync as readFileSync5 } from "fs";
3918
+ import { readFileSync as readFileSync6 } from "fs";
3878
3919
  import { dirname as dirname5, relative as relative3, sep as sep2 } from "path";
3879
- import { parseSource as parseSource4 } from "@patterkit/core";
3920
+ import { parseSource as parseSource5 } from "@patterkit/core";
3880
3921
  import { ARCHIVE_ENTRY_OPTS } from "@wildwinter/toolkit/archive";
3881
3922
  var SHARD_EXTENSIONS = [".patterflow", ".patterloc", ".patterx", ".patterproj"];
3882
3923
  var ENTRY_OPTS = ARCHIVE_ENTRY_OPTS;
3883
3924
  async function runPack(startPath) {
3884
3925
  const projectFile = findProjectFile(startPath);
3885
3926
  const root = dirname5(projectFile);
3886
- const project = parseSource4(readFileSync5(projectFile, "utf8"));
3927
+ const project = parseSource5(readFileSync6(projectFile, "utf8"));
3887
3928
  const unresolved = sidecarIssues(walkFiles(root, CONFLICT_SIDECAR));
3888
3929
  if (unresolved.length) {
3889
3930
  throw new Error(
@@ -3901,7 +3942,7 @@ async function runPack(startPath) {
3901
3942
  };
3902
3943
  const zip = new JSZip();
3903
3944
  zip.file("patter.manifest.json", JSON.stringify(manifest, null, 2) + "\n", ENTRY_OPTS);
3904
- for (const f of files) zip.file(f.rel, readFileSync5(f.abs, "utf8"), ENTRY_OPTS);
3945
+ for (const f of files) zip.file(f.rel, readFileSync6(f.abs, "utf8"), ENTRY_OPTS);
3905
3946
  for (const f of scopes) zip.file(`${GAME_SCOPES_DIR}/${f.fileName}`, f.text, ENTRY_OPTS);
3906
3947
  return zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE", streamFiles: false });
3907
3948
  }
@@ -3909,8 +3950,8 @@ async function runPack(startPath) {
3909
3950
  // src/unpack.ts
3910
3951
  import JSZip2 from "jszip";
3911
3952
  import { join as join7, resolve as resolve5, sep as sep3 } from "path";
3912
- import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
3913
- import { parseSource as parseSource5, canonicalStringify as canonicalStringify8 } from "@patterkit/core";
3953
+ import { existsSync as existsSync4, readFileSync as readFileSync7 } from "fs";
3954
+ import { parseSource as parseSource6, canonicalStringify as canonicalStringify9 } from "@patterkit/core";
3914
3955
  import { isUnsafeEntry } from "@wildwinter/toolkit/archive";
3915
3956
  import { SCOPES_FILE_SUFFIX as SCOPES_FILE_SUFFIX2 } from "@wildwinter/scoperegistry/scopes";
3916
3957
  import { isUnsafeEntry as isUnsafeEntry2 } from "@wildwinter/toolkit/archive";
@@ -3956,7 +3997,7 @@ function checkProvenance(returned, base, target) {
3956
3997
  }
3957
3998
  function targetProjectId(projectDir) {
3958
3999
  try {
3959
- const pf = parseSource5(readFileSync6(findProjectFile(projectDir), "utf8"));
4000
+ const pf = parseSource6(readFileSync7(findProjectFile(projectDir), "utf8"));
3960
4001
  return pf.project?.id;
3961
4002
  } catch {
3962
4003
  return void 0;
@@ -3983,17 +4024,17 @@ async function runUnpackMerge(returnedBytes, baseBytes, projectDir) {
3983
4024
  }
3984
4025
  const readSide = (side, text) => {
3985
4026
  try {
3986
- return parseSource5(text);
4027
+ return parseSource6(text);
3987
4028
  } catch (e) {
3988
4029
  throw new Error(`${rel}: the ${side} copy is not readable Patter source - ${e instanceof Error ? e.message : String(e)}`);
3989
4030
  }
3990
4031
  };
3991
- const oursObj = readSide("ours", readFileSync6(outPath, "utf8"));
4032
+ const oursObj = readSide("ours", readFileSync7(outPath, "utf8"));
3992
4033
  const theirsObj = readSide("theirs", theirText);
3993
4034
  const baseText = base.get(rel);
3994
4035
  const baseObj = baseText !== void 0 ? readSide("base", baseText) : {};
3995
4036
  const result = runMerge(baseObj, oursObj, theirsObj);
3996
- writes.push({ path: outPath, content: canonicalStringify8(result.merged) });
4037
+ writes.push({ path: outPath, content: canonicalStringify9(result.merged) });
3997
4038
  if (rel === projectRel && baseText !== void 0) {
3998
4039
  projectSides = { ours: oursObj, base: baseObj, theirs: theirsObj, merged: result.merged };
3999
4040
  }
@@ -4064,6 +4105,7 @@ export {
4064
4105
  patterScopesFile,
4065
4106
  patterScopesStale,
4066
4107
  patterScopesWrite,
4108
+ planPins,
4067
4109
  planReturnedWorld,
4068
4110
  planShareScopes,
4069
4111
  planWorldSave,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/ops",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "The shared operations layer: every project operation (load, validate, export, format, play, ...) as a pure function, consumed by the patter CLI and Patterpad alike.",
5
5
  "type": "module",
6
6
  "license": "MIT",