@storylet-studio/play-helpers 0.6.0 → 0.8.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.cts CHANGED
@@ -210,18 +210,17 @@ type LiveBundleResult =
210
210
  error: string;
211
211
  };
212
212
  /**
213
- * Apply a bundle the editor pushed over the Live Link: `new Engine(parsed,
214
- * opts)` then `loadGame(engine.saveGame())`, returning the new engine. Never
215
- * throws; a failure (unparseable JSON, a bundle the runtime rejects, a
216
- * different project) comes back as `{ ok: false, error }` and the old engine
217
- * is untouched.
213
+ * Apply a bundle the editor pushed over the Live Link through the engine's own
214
+ * `hotSwap`, returning the replacement. Never throws; a failure (unparseable
215
+ * JSON, a bundle the runtime rejects, a different project) comes back as
216
+ * `{ ok: false, error }` and the old engine is left as it was.
218
217
  *
219
- * `opts` are the options the old engine was created with. An engine does
220
- * not expose its seed, and it does not matter here: the save envelope carries
221
- * each flow's PRNG state, so `loadGame` resumes the draw sequences exactly
222
- * where they were and `seed` only shapes fresh flows. `log` does matter (the
223
- * retained log is per flow), and so does `world` (the host's binding does not
224
- * ride the envelope), so pass them if you had them.
218
+ * Works whether the engine made its own registry or was given the game's: on
219
+ * the game's registry the old engine hands its keys to the replacement, which a
220
+ * plain save and load into a second engine cannot do (the two would clash).
221
+ *
222
+ * The engine remembers the options it was built with (seed, log, world,
223
+ * registry), so `opts` is only for overriding one of them for the replacement.
225
224
  */
226
225
  declare function applyLiveBundle(engine: Engine, bundleJson: string, opts?: EngineOptions): LiveBundleResult;
227
226
 
package/dist/index.d.ts CHANGED
@@ -210,18 +210,17 @@ type LiveBundleResult =
210
210
  error: string;
211
211
  };
212
212
  /**
213
- * Apply a bundle the editor pushed over the Live Link: `new Engine(parsed,
214
- * opts)` then `loadGame(engine.saveGame())`, returning the new engine. Never
215
- * throws; a failure (unparseable JSON, a bundle the runtime rejects, a
216
- * different project) comes back as `{ ok: false, error }` and the old engine
217
- * is untouched.
213
+ * Apply a bundle the editor pushed over the Live Link through the engine's own
214
+ * `hotSwap`, returning the replacement. Never throws; a failure (unparseable
215
+ * JSON, a bundle the runtime rejects, a different project) comes back as
216
+ * `{ ok: false, error }` and the old engine is left as it was.
218
217
  *
219
- * `opts` are the options the old engine was created with. An engine does
220
- * not expose its seed, and it does not matter here: the save envelope carries
221
- * each flow's PRNG state, so `loadGame` resumes the draw sequences exactly
222
- * where they were and `seed` only shapes fresh flows. `log` does matter (the
223
- * retained log is per flow), and so does `world` (the host's binding does not
224
- * ride the envelope), so pass them if you had them.
218
+ * Works whether the engine made its own registry or was given the game's: on
219
+ * the game's registry the old engine hands its keys to the replacement, which a
220
+ * plain save and load into a second engine cannot do (the two would clash).
221
+ *
222
+ * The engine remembers the options it was built with (seed, log, world,
223
+ * registry), so `opts` is only for overriding one of them for the replacement.
225
224
  */
226
225
  declare function applyLiveBundle(engine: Engine, bundleJson: string, opts?: EngineOptions): LiveBundleResult;
227
226
 
package/dist/index.js CHANGED
@@ -93,6 +93,13 @@ var PropertyBag = class _PropertyBag {
93
93
  get(name) {
94
94
  return this.values[this.norm(name)];
95
95
  }
96
+ /** A name as this bag keys it: its normalisation policy applied. The registry
97
+ * uses it to key quality ladders and the validation schema the bag's own way,
98
+ * so a case-significant (identity) bag is not quietly folded to lower case
99
+ * one layer up. */
100
+ normalise(name) {
101
+ return this.norm(name);
102
+ }
96
103
  /** Write a property. Engine writes (the default) notify subscribers;
97
104
  * pass `silent: true` for a host write, which reaches only the audit
98
105
  * hook. Throws on a read-only property unless the caller says it is the
@@ -234,7 +241,8 @@ function createStateLogger2(engine, flow, opts = {}) {
234
241
  }
235
242
 
236
243
  // ../model/src/index.ts
237
- var SAVE_SCHEMA = "storylets/save@1";
244
+ var SAVE_SCHEMA = "storylets/save@2";
245
+ var SAVE_SCHEMA_V1 = "storylets/save@1";
238
246
  var SAVEFILE_SCHEMA = "storylets/savefile@1";
239
247
 
240
248
  // src/save.ts
@@ -249,7 +257,7 @@ function saveState(engine, world) {
249
257
  };
250
258
  }
251
259
  function loadState(engine, file) {
252
- if (!file || typeof file !== "object" || file.schema !== SAVEFILE_SCHEMA || file.engine?.schema !== SAVE_SCHEMA) {
260
+ if (!file || typeof file !== "object" || file.schema !== SAVEFILE_SCHEMA || file.engine?.schema !== SAVE_SCHEMA && file.engine?.schema !== SAVE_SCHEMA_V1) {
253
261
  throw new Error(`not a storylets save (expected schema "${SAVEFILE_SCHEMA}")`);
254
262
  }
255
263
  engine.loadGame(file.engine);
@@ -333,8 +341,9 @@ function formatLogBody(e, flow) {
333
341
  }
334
342
  case "evict":
335
343
  return `${stamp}evict ${e.card} from ${e.hand} (${e.reason})`;
344
+ // A card played with none ("") has no outcome to name.
336
345
  case "play":
337
- return `${stamp}play ${e.card} -> ${e.outcome}`;
346
+ return `${stamp}play ${e.card}${e.outcome === "" ? "" : ` -> ${e.outcome}`}`;
338
347
  case "write":
339
348
  return `${stamp}write ${e.path}: ${showVal(e.prev)} -> ${showVal(e.value)}`;
340
349
  case "turns":
@@ -944,7 +953,7 @@ function createLiveLink(opts) {
944
953
  }
945
954
 
946
955
  // src/refresh.ts
947
- import { Engine } from "@storylet-studio/runtime";
956
+ import "@storylet-studio/runtime";
948
957
  function applyLiveBundle(engine, bundleJson, opts = {}) {
949
958
  let bundle;
950
959
  try {
@@ -953,8 +962,7 @@ function applyLiveBundle(engine, bundleJson, opts = {}) {
953
962
  return { ok: false, error: "pushed bundle is not valid JSON" };
954
963
  }
955
964
  try {
956
- const next = new Engine(bundle, opts);
957
- next.loadGame(engine.saveGame());
965
+ const { engine: next } = engine.hotSwap(bundle, opts);
958
966
  return { ok: true, engine: next, bundle };
959
967
  } catch (e) {
960
968
  return { ok: false, error: e instanceof Error ? e.message : String(e) };