@patterkit/play-helpers 0.4.0 → 0.5.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.cjs CHANGED
@@ -28,7 +28,7 @@ __export(index_exports, {
28
28
  createPropertyInspector: () => createPropertyInspector,
29
29
  createStateLogger: () => createStateLogger,
30
30
  deserializeState: () => deserializeState,
31
- diffState: () => diffState,
31
+ diffState: () => import_scoperegistry.diffState,
32
32
  getProperty: () => getProperty,
33
33
  loadState: () => loadState,
34
34
  saveState: () => saveState,
@@ -69,7 +69,7 @@ function setProperties(engine, values) {
69
69
  }
70
70
 
71
71
  // src/logger.ts
72
- var eq = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
72
+ var import_scoperegistry = require("@wildwinter/scoperegistry");
73
73
  function snapshotState(engine) {
74
74
  const save = engine.saveGame();
75
75
  const out = {};
@@ -85,16 +85,6 @@ function snapshotState(engine) {
85
85
  }
86
86
  return out;
87
87
  }
88
- function diffState(prev, next) {
89
- const changes = [];
90
- const keys = [.../* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)])].sort();
91
- for (const path of keys) {
92
- const from = prev[path], to = next[path];
93
- if (!eq(from, to)) changes.push({ path, ...from !== void 0 ? { from } : {}, ...to !== void 0 ? { to } : {} });
94
- }
95
- return changes;
96
- }
97
- var fmt = (v) => v === void 0 ? "<unset>" : JSON.stringify(v);
98
88
  function describeStep(step) {
99
89
  switch (step.type) {
100
90
  case "line":
@@ -110,21 +100,29 @@ function describeStep(step) {
110
100
  }
111
101
  }
112
102
  var gd = (data) => data ? ` gameData=${JSON.stringify(data)}` : "";
103
+ function visitState(engine) {
104
+ const save = engine.saveGame();
105
+ const out = {};
106
+ for (const [id, n] of Object.entries(save.sharedVisits)) out[`visit:${id}`] = n;
107
+ for (const [fid, snap] of Object.entries(save.flows)) {
108
+ for (const [id, n] of Object.entries(snap.visits)) out[`${fid}/visit:${id}`] = n;
109
+ }
110
+ return out;
111
+ }
113
112
  function createStateLogger(engine, opts = {}) {
114
- const sink = opts.sink ?? ((line) => console.log(line));
115
113
  const tag = opts.label ? `[${opts.label}] ` : "";
116
- let baseline = snapshotState(engine);
114
+ const kernel = (0, import_scoperegistry.createStateLogger)({
115
+ // Re-read on every capture: openFlow and loadGame both replace bags, and the kernel
116
+ // re-mounts whatever it is handed.
117
+ mounts: () => [...engine.listBags(), ...engine.flows().flatMap((f) => f.listBags())],
118
+ extra: () => visitState(engine)
119
+ }, { sink: opts.sink, label: tag });
117
120
  return {
118
121
  snapshot: () => snapshotState(engine),
119
- capture() {
120
- const next = snapshotState(engine);
121
- const changes = diffState(baseline, next);
122
- baseline = next;
123
- for (const c of changes) sink(`${tag}${c.path}: ${fmt(c.from)} -> ${fmt(c.to)}`);
124
- return changes;
125
- },
122
+ capture: () => kernel.capture(),
123
+ dispose: () => kernel.dispose(),
126
124
  logStep(step) {
127
- sink(`${tag}${describeStep(step)}`);
125
+ (opts.sink ?? ((l) => console.log(l)))(`${tag}${describeStep(step)}`);
128
126
  }
129
127
  };
130
128
  }
package/dist/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { SaveGame, Engine, StepResult, Bundle as Bundle$1, BundleDescription } from '@patterkit/runtime';
2
+ import { StateSnapshot, StateChange, ScalarValue as ScalarValue$1 } from '@wildwinter/scoperegistry';
3
+ export { StateChange, StateSnapshot, diffState } from '@wildwinter/scoperegistry';
2
4
 
3
5
  declare const SAVE_SCHEMA = "patter/save@0";
4
6
  interface SaveEnvelope {
@@ -24,20 +26,14 @@ declare function setProperty(engine: Engine, ref: string, value: PropertyValue):
24
26
  /** Set many at once, e.g. `setProperties(engine, { "@hp": 10, "@scene.locked": false })`. */
25
27
  declare function setProperties(engine: Engine, values: Record<string, PropertyValue>): void;
26
28
 
27
- /** A flattened runtime-state value (a Patter scalar: number / boolean / string / string[] flags). */
28
- type StateValue = NonNullable<ReturnType<Engine["getProperty"]>>;
29
- /** A flattened snapshot: dotted path -> value. Paths: `@patter.x`, `@scene:scene.x`, `visit:nodeId`,
30
- * and `flow/...` for a flow's not-shared locals. */
31
- type StateSnapshot = Record<string, StateValue>;
32
- interface StateChange {
33
- path: string;
34
- from?: StateValue;
35
- to?: StateValue;
36
- }
37
- /** Flatten the engine's whole-game state into a path -> value map (shared scopes + every live flow). */
29
+ /** A flattened runtime-state value: a Patter scalar. The kernel calls this `ScalarValue`;
30
+ * the alias stays because it is exported API and reads in Patter's own vocabulary. */
31
+ type StateValue = ScalarValue$1;
32
+ /** Flatten the engine's whole-game state into a path -> value map (shared scopes + every live
33
+ * flow), off `saveGame()`. The logger no longer diffs this - it mounts the bags directly - but
34
+ * it stays as the public "what is the state right now" call, and as the definition of the path
35
+ * space the mounts compose. */
38
36
  declare function snapshotState(engine: Engine): StateSnapshot;
39
- /** The sorted set of paths that differ between two snapshots (added / removed / changed). */
40
- declare function diffState(prev: StateSnapshot, next: StateSnapshot): StateChange[];
41
37
  interface StateLoggerOptions {
42
38
  /** Where lines go; defaults to `console.log`. */
43
39
  sink?: (line: string) => void;
@@ -47,12 +43,16 @@ interface StateLoggerOptions {
47
43
  interface StateLogger {
48
44
  /** The current flattened state (no logging). */
49
45
  snapshot(): StateSnapshot;
50
- /** Diff since the last capture, log each change, and re-baseline. Returns the changes. */
46
+ /** Everything since the last capture: the property writes already logged as they landed,
47
+ * plus the visit counts, diffed and re-baselined. */
51
48
  capture(): StateChange[];
52
49
  /** Trace one played step (line / text / game-event / choice / end), including any `gameData`. */
53
50
  logStep(step: StepResult): void;
51
+ /** Unhook the bag auditors. The logger is inert afterwards. */
52
+ dispose(): void;
54
53
  }
55
- /** Create a state logger over an engine. Call `capture()` after each `advance`/`choose` to log mutations. */
54
+ /** Create a state logger over an engine. Property writes log as they land; call `capture()`
55
+ * after each `advance`/`choose` to pick up the visit counts and re-baseline. */
56
56
  declare function createStateLogger(engine: Engine, opts?: StateLoggerOptions): StateLogger;
57
57
 
58
58
  /** A minimal structural type for a WebSocket implementation (browsers + Node 21+ have a global one). */
@@ -490,4 +490,4 @@ interface AudioResolver {
490
490
  */
491
491
  declare function createAudioResolver(manifestJson: string, basePath: string): AudioResolver;
492
492
 
493
- export { type AudioResolver, type BundleInspector, type BundleInspectorOptions, type BundleSection, type DebugLink, type DebugLinkOptions, type DebugSocketLike, type LiveBundleResult, type PropertyInspector, type PropertyInspectorOptions, type PropertyValue, SAVE_SCHEMA, type SaveEnvelope, type StateChange, type StateLogger, type StateLoggerOptions, type StateSnapshot, type StateValue, applyLiveBundle, createAudioResolver, createBundleInspector, createDebugLink, createPropertyInspector, createStateLogger, deserializeState, diffState, getProperty, loadState, saveState, serializeState, setProperties, setProperty, snapshotState };
493
+ export { type AudioResolver, type BundleInspector, type BundleInspectorOptions, type BundleSection, type DebugLink, type DebugLinkOptions, type DebugSocketLike, type LiveBundleResult, type PropertyInspector, type PropertyInspectorOptions, type PropertyValue, SAVE_SCHEMA, type SaveEnvelope, type StateLogger, type StateLoggerOptions, type StateValue, applyLiveBundle, createAudioResolver, createBundleInspector, createDebugLink, createPropertyInspector, createStateLogger, deserializeState, getProperty, loadState, saveState, serializeState, setProperties, setProperty, snapshotState };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { SaveGame, Engine, StepResult, Bundle as Bundle$1, BundleDescription } from '@patterkit/runtime';
2
+ import { StateSnapshot, StateChange, ScalarValue as ScalarValue$1 } from '@wildwinter/scoperegistry';
3
+ export { StateChange, StateSnapshot, diffState } from '@wildwinter/scoperegistry';
2
4
 
3
5
  declare const SAVE_SCHEMA = "patter/save@0";
4
6
  interface SaveEnvelope {
@@ -24,20 +26,14 @@ declare function setProperty(engine: Engine, ref: string, value: PropertyValue):
24
26
  /** Set many at once, e.g. `setProperties(engine, { "@hp": 10, "@scene.locked": false })`. */
25
27
  declare function setProperties(engine: Engine, values: Record<string, PropertyValue>): void;
26
28
 
27
- /** A flattened runtime-state value (a Patter scalar: number / boolean / string / string[] flags). */
28
- type StateValue = NonNullable<ReturnType<Engine["getProperty"]>>;
29
- /** A flattened snapshot: dotted path -> value. Paths: `@patter.x`, `@scene:scene.x`, `visit:nodeId`,
30
- * and `flow/...` for a flow's not-shared locals. */
31
- type StateSnapshot = Record<string, StateValue>;
32
- interface StateChange {
33
- path: string;
34
- from?: StateValue;
35
- to?: StateValue;
36
- }
37
- /** Flatten the engine's whole-game state into a path -> value map (shared scopes + every live flow). */
29
+ /** A flattened runtime-state value: a Patter scalar. The kernel calls this `ScalarValue`;
30
+ * the alias stays because it is exported API and reads in Patter's own vocabulary. */
31
+ type StateValue = ScalarValue$1;
32
+ /** Flatten the engine's whole-game state into a path -> value map (shared scopes + every live
33
+ * flow), off `saveGame()`. The logger no longer diffs this - it mounts the bags directly - but
34
+ * it stays as the public "what is the state right now" call, and as the definition of the path
35
+ * space the mounts compose. */
38
36
  declare function snapshotState(engine: Engine): StateSnapshot;
39
- /** The sorted set of paths that differ between two snapshots (added / removed / changed). */
40
- declare function diffState(prev: StateSnapshot, next: StateSnapshot): StateChange[];
41
37
  interface StateLoggerOptions {
42
38
  /** Where lines go; defaults to `console.log`. */
43
39
  sink?: (line: string) => void;
@@ -47,12 +43,16 @@ interface StateLoggerOptions {
47
43
  interface StateLogger {
48
44
  /** The current flattened state (no logging). */
49
45
  snapshot(): StateSnapshot;
50
- /** Diff since the last capture, log each change, and re-baseline. Returns the changes. */
46
+ /** Everything since the last capture: the property writes already logged as they landed,
47
+ * plus the visit counts, diffed and re-baselined. */
51
48
  capture(): StateChange[];
52
49
  /** Trace one played step (line / text / game-event / choice / end), including any `gameData`. */
53
50
  logStep(step: StepResult): void;
51
+ /** Unhook the bag auditors. The logger is inert afterwards. */
52
+ dispose(): void;
54
53
  }
55
- /** Create a state logger over an engine. Call `capture()` after each `advance`/`choose` to log mutations. */
54
+ /** Create a state logger over an engine. Property writes log as they land; call `capture()`
55
+ * after each `advance`/`choose` to pick up the visit counts and re-baseline. */
56
56
  declare function createStateLogger(engine: Engine, opts?: StateLoggerOptions): StateLogger;
57
57
 
58
58
  /** A minimal structural type for a WebSocket implementation (browsers + Node 21+ have a global one). */
@@ -490,4 +490,4 @@ interface AudioResolver {
490
490
  */
491
491
  declare function createAudioResolver(manifestJson: string, basePath: string): AudioResolver;
492
492
 
493
- export { type AudioResolver, type BundleInspector, type BundleInspectorOptions, type BundleSection, type DebugLink, type DebugLinkOptions, type DebugSocketLike, type LiveBundleResult, type PropertyInspector, type PropertyInspectorOptions, type PropertyValue, SAVE_SCHEMA, type SaveEnvelope, type StateChange, type StateLogger, type StateLoggerOptions, type StateSnapshot, type StateValue, applyLiveBundle, createAudioResolver, createBundleInspector, createDebugLink, createPropertyInspector, createStateLogger, deserializeState, diffState, getProperty, loadState, saveState, serializeState, setProperties, setProperty, snapshotState };
493
+ export { type AudioResolver, type BundleInspector, type BundleInspectorOptions, type BundleSection, type DebugLink, type DebugLinkOptions, type DebugSocketLike, type LiveBundleResult, type PropertyInspector, type PropertyInspectorOptions, type PropertyValue, SAVE_SCHEMA, type SaveEnvelope, type StateLogger, type StateLoggerOptions, type StateValue, applyLiveBundle, createAudioResolver, createBundleInspector, createDebugLink, createPropertyInspector, createStateLogger, deserializeState, getProperty, loadState, saveState, serializeState, setProperties, setProperty, snapshotState };
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ function setProperties(engine, values) {
28
28
  }
29
29
 
30
30
  // src/logger.ts
31
- var eq = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
31
+ import { createStateLogger as createKernelStateLogger, diffState } from "@wildwinter/scoperegistry";
32
32
  function snapshotState(engine) {
33
33
  const save = engine.saveGame();
34
34
  const out = {};
@@ -44,16 +44,6 @@ function snapshotState(engine) {
44
44
  }
45
45
  return out;
46
46
  }
47
- function diffState(prev, next) {
48
- const changes = [];
49
- const keys = [.../* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)])].sort();
50
- for (const path of keys) {
51
- const from = prev[path], to = next[path];
52
- if (!eq(from, to)) changes.push({ path, ...from !== void 0 ? { from } : {}, ...to !== void 0 ? { to } : {} });
53
- }
54
- return changes;
55
- }
56
- var fmt = (v) => v === void 0 ? "<unset>" : JSON.stringify(v);
57
47
  function describeStep(step) {
58
48
  switch (step.type) {
59
49
  case "line":
@@ -69,21 +59,29 @@ function describeStep(step) {
69
59
  }
70
60
  }
71
61
  var gd = (data) => data ? ` gameData=${JSON.stringify(data)}` : "";
62
+ function visitState(engine) {
63
+ const save = engine.saveGame();
64
+ const out = {};
65
+ for (const [id, n] of Object.entries(save.sharedVisits)) out[`visit:${id}`] = n;
66
+ for (const [fid, snap] of Object.entries(save.flows)) {
67
+ for (const [id, n] of Object.entries(snap.visits)) out[`${fid}/visit:${id}`] = n;
68
+ }
69
+ return out;
70
+ }
72
71
  function createStateLogger(engine, opts = {}) {
73
- const sink = opts.sink ?? ((line) => console.log(line));
74
72
  const tag = opts.label ? `[${opts.label}] ` : "";
75
- let baseline = snapshotState(engine);
73
+ const kernel = createKernelStateLogger({
74
+ // Re-read on every capture: openFlow and loadGame both replace bags, and the kernel
75
+ // re-mounts whatever it is handed.
76
+ mounts: () => [...engine.listBags(), ...engine.flows().flatMap((f) => f.listBags())],
77
+ extra: () => visitState(engine)
78
+ }, { sink: opts.sink, label: tag });
76
79
  return {
77
80
  snapshot: () => snapshotState(engine),
78
- capture() {
79
- const next = snapshotState(engine);
80
- const changes = diffState(baseline, next);
81
- baseline = next;
82
- for (const c of changes) sink(`${tag}${c.path}: ${fmt(c.from)} -> ${fmt(c.to)}`);
83
- return changes;
84
- },
81
+ capture: () => kernel.capture(),
82
+ dispose: () => kernel.dispose(),
85
83
  logStep(step) {
86
- sink(`${tag}${describeStep(step)}`);
84
+ (opts.sink ?? ((l) => console.log(l)))(`${tag}${describeStep(step)}`);
87
85
  }
88
86
  };
89
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/play-helpers",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Thin game-integration helpers around @patterkit/runtime: save/load serialisation, runtime property setters, a state logger, the Patterpad Live Link client + hot reload, a property inspector, and audio resolution. The Patterplay JS companion.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,6 +34,7 @@
34
34
  "build": "tsup src/index.ts --format esm,cjs --dts --clean"
35
35
  },
36
36
  "dependencies": {
37
- "@patterkit/runtime": "0.9.0"
37
+ "@patterkit/runtime": "0.10.0",
38
+ "@wildwinter/scoperegistry": "^0.5.0"
38
39
  }
39
40
  }