@jigs.design/server 0.3.2 → 0.3.4

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/compile.js CHANGED
@@ -3,7 +3,7 @@ import { JigsCompileError, JigsError, fatalDiagnostics } from "./errors.js";
3
3
  import { inputDiagnostics } from "./inputs.js";
4
4
  import { outputDiagnostics } from "./outputs.js";
5
5
  import { splitPragma, versionDiagnostics } from "./pragma.js";
6
- import { compileJigs, compileLibrary, spanIn } from "@jigs.design/compiler";
6
+ import { compileJigs, compileLibrary, nearest, spanIn } from "@jigs.design/compiler";
7
7
  import { TOOL_RESULTS } from "@jigs.design/prompt";
8
8
  import { componentSchema } from "@jigs.design/host";
9
9
  import { PROGRAM_FORMAT } from "@jigs.design/protocol";
@@ -69,6 +69,18 @@ const KIND_NAMES = {
69
69
  function hookFor(kind) {
70
70
  return kind === "query" || kind === "data" ? "useQuery" : "useMutation";
71
71
  }
72
+ /**
73
+ * The host's name nearest to `name`, for an unknown one: one `hook` can
74
+ * read first, so the fix is a rename and not a second error, else any.
75
+ */
76
+ function nearestProcedure(host, name, hook) {
77
+ const names = Object.keys(host.procedures);
78
+ const fitting = names.filter((entry) => {
79
+ const procedure = host.procedures[entry];
80
+ return procedure !== void 0 && hookFor(procedure.kind) === hook;
81
+ });
82
+ return nearest(name, fitting, 1)[0] ?? nearest(name, names, 1)[0];
83
+ }
72
84
  function procedureDiagnostics(host, source, uses) {
73
85
  const diagnostics = [];
74
86
  const report = (use, code, message) => {
@@ -95,7 +107,8 @@ function procedureDiagnostics(host, source, uses) {
95
107
  }
96
108
  const procedure = Object.hasOwn(host.procedures, use.name) ? host.procedures[use.name] : void 0;
97
109
  if (procedure === void 0) {
98
- report(use, "unknown_procedure", `"${use.name}" is not a query, mutation, action or provided data on this host; its handle reports UNKNOWN_PROCEDURE`);
110
+ const near = nearestProcedure(host, use.name, use.hook);
111
+ report(use, "unknown_procedure", `"${use.name}" is not a query, mutation, action or provided data on this host; its handle reports UNKNOWN_PROCEDURE${near === void 0 ? "" : `. Did you mean "${near}"?`}`);
99
112
  continue;
100
113
  }
101
114
  const expected = hookFor(procedure.kind);
package/dist/core.js CHANGED
@@ -26,7 +26,17 @@ const COMPILER_PROBE = `{@body const [count, setCount] = useState(0)}
26
26
  **done**
27
27
  </if>`;
28
28
  /** What `resolvePage` must return, checked, since a plain-JavaScript one may not. */
29
- const savedPageSchema = z.object({ source: z.string() });
29
+ const savedPageSchema = z.object({
30
+ data: z.record(z.string(), z.json()).optional(),
31
+ source: z.string()
32
+ });
33
+ /**
34
+ * The most JSON a saved page's `data` may be, in characters, the order of
35
+ * `MAX_CONTENT`: the app stored it, so past it the page is `INTERNAL`.
36
+ */
37
+ const MAX_PAGE_DATA = 256e3;
38
+ /** What a page without provided data carries: one object, shared and frozen. */
39
+ const NO_DATA = Object.freeze({});
30
40
  /** The strict rule: an error or warning diagnostic throws. */
31
41
  function strictly(subject, compiled) {
32
42
  const fatal = fatalDiagnostics(compiled.diagnostics);
@@ -414,11 +424,13 @@ function createJigsServer(options) {
414
424
  const subject = `Jig "${entry.name}"`;
415
425
  if (entry.kind === "document") startupPages.set(entry.name, {
416
426
  ...strictly(subject, compileStored(entry.source)),
427
+ data: NO_DATA,
417
428
  origin: "shipped",
418
429
  source: entry.source
419
430
  });
420
431
  else if (!checker.needsProps(entry.name)) startupPages.set(entry.name, {
421
432
  ...strictly(subject, compileDefine(entry.name, {})),
433
+ data: NO_DATA,
422
434
  origin: "shipped",
423
435
  source: checker.pageBody(entry.name, {})
424
436
  });
@@ -432,6 +444,7 @@ function createJigsServer(options) {
432
444
  const compiled = await cached("page", body, () => compileDefine(entry.name, props));
433
445
  return {
434
446
  ...strictly(`Page "${entry.name}"`, compiled),
447
+ data: NO_DATA,
435
448
  origin: "shipped",
436
449
  source: body
437
450
  };
@@ -443,7 +456,10 @@ function createJigsServer(options) {
443
456
  });
444
457
  return new JigsError("INTERNAL", INTERNAL_MESSAGE);
445
458
  };
446
- /** The source `resolvePage` finds for `call.ctx`, checked; null when it finds none. */
459
+ /**
460
+ * The page `resolvePage` finds for `call.ctx`, checked: its source and
461
+ * the data it keeps (empty without). Null when it finds none.
462
+ */
447
463
  const savedSource = async (name, props, lookup) => {
448
464
  if (options.resolvePage === void 0) return null;
449
465
  let found;
@@ -458,21 +474,26 @@ function createJigsServer(options) {
458
474
  }
459
475
  if (found === null) return null;
460
476
  const checked = savedPageSchema.safeParse(found);
461
- if (!checked.success) throw internal(/* @__PURE__ */ new Error(`resolvePage returned ${String(found)} for "${name}"; return { source } or null`), name);
462
- const source = checked.data.source;
463
- if (source.length > 2e5) throw internal(/* @__PURE__ */ new Error(`The source resolvePage returned for "${name}" is longer than ${MAX_CONTENT} characters`), name);
477
+ if (!checked.success) throw internal(/* @__PURE__ */ new Error(`resolvePage returned ${String(found)} for "${name}"; return { source, data? } or null`), name);
478
+ const { data } = checked.data;
479
+ if (checked.data.source.length > 2e5) throw internal(/* @__PURE__ */ new Error(`The source resolvePage returned for "${name}" is longer than ${MAX_CONTENT} characters`), name);
480
+ if (data !== void 0 && JSON.stringify(data).length > MAX_PAGE_DATA) throw internal(/* @__PURE__ */ new Error(`The data resolvePage returned for "${name}" is longer than ${MAX_PAGE_DATA} characters of JSON`), name);
464
481
  noProps(name, props);
465
- return source;
482
+ return {
483
+ data: data ?? NO_DATA,
484
+ source: checked.data.source
485
+ };
466
486
  };
467
487
  /** A page `resolvePage` finds for `call.ctx`, compiled leniently unless `strict`. */
468
488
  const savedPage = async (name, props, lookup) => {
469
- const source = await savedSource(name, props, lookup);
470
- if (source === null) return null;
471
- const compiled = await compileCached(source);
489
+ const saved = await savedSource(name, props, lookup);
490
+ if (saved === null) return null;
491
+ const compiled = await compileCached(saved.source);
472
492
  return {
473
493
  ...lookup.strict === true ? strictly(`Page "${name}"`, compiled) : compiled,
494
+ data: saved.data,
474
495
  origin: "saved",
475
- source
496
+ source: saved.source
476
497
  };
477
498
  };
478
499
  const resolve = async (name, props = {}, lookup) => {
@@ -526,6 +547,7 @@ function createJigsServer(options) {
526
547
  } else try {
527
548
  const resolved = await page(name, props, context);
528
549
  outcome = resolved === null ? { error: notFound } : {
550
+ data: resolved.data,
529
551
  diagnostics: resolved.diagnostics,
530
552
  program: resolved.program,
531
553
  version
@@ -560,7 +582,10 @@ function createJigsServer(options) {
560
582
  */
561
583
  const pageSource = async (name, props, context) => {
562
584
  const entry = jigsByName.get(name);
563
- if (entry === void 0) return savedSource(name, props, context);
585
+ if (entry === void 0) {
586
+ const saved = await savedSource(name, props, context);
587
+ return saved === null ? null : saved.source;
588
+ }
564
589
  const shippedName = name;
565
590
  if (!entry.page || !pageAllowed(context.ctx, shippedName, props)) return null;
566
591
  if (entry.kind === "document") {
package/dist/types.d.ts CHANGED
@@ -289,8 +289,15 @@ export interface ErrorInfo {
289
289
  */
290
290
  type: "call" | "chat" | "events" | "page" | "session";
291
291
  }
292
- /** A page `resolvePage` found: the source as the app stored it. */
292
+ /** A page `resolvePage` found: the source as the app stored it, and what it reads. */
293
293
  export interface SavedPage {
294
+ /**
295
+ * Provided data the page is drawn with, by slot: what the app kept with
296
+ * it, such as the tool results a chat block had. Fixed to the page: it
297
+ * travels beside the program and is cached with it. Not parsed against
298
+ * the slots' schemas; `jigs.data` checks a value before it is stored.
299
+ */
300
+ data?: Readonly<Record<string, JsonValue>>;
294
301
  /** The source, with its pragma line when the app kept it. */
295
302
  source: string;
296
303
  }
@@ -298,6 +305,7 @@ export interface SavedPage {
298
305
  * Finds a page the host does not ship, by name, for the caller `ctx` names.
299
306
  * `null` is "no such page for this caller". Runs on every request for the
300
307
  * name; cache in it if the store is slow. Throw a `JigsError` to refuse.
308
+ * `data` is optional: the provided slots the page keeps, as JSON.
301
309
  */
302
310
  export type PageResolver<TContext> = (name: string, call: {
303
311
  ctx: TContext;
@@ -602,6 +610,13 @@ export interface ChatTurn {
602
610
  export interface ResolvedPage {
603
611
  /** The design checks' advice, numbered like `diagnostics`. */
604
612
  advice: ReadonlyArray<Advice>;
613
+ /**
614
+ * Provided data the page is drawn with, by slot: what `resolvePage`
615
+ * returned beside the source. Empty for a shipped page, whose route
616
+ * supplies its slots. Pass it to the view with the app's own slots:
617
+ * `data={{ ...page.data, ...own }}`.
618
+ */
619
+ data: Readonly<Record<string, JsonValue>>;
605
620
  /**
606
621
  * Never an error or warning for a shipped page, or a saved one compiled
607
622
  * with `strict`. A saved page is lenient by default: these say what
@@ -703,8 +718,8 @@ export interface JigsServer<TContext, TPageName extends string = never> {
703
718
  /**
704
719
  * The start of `prompt(mode)` that is the same on every turn: the
705
720
  * language and the catalog, before the instructions and procedures. Mark
706
- * it for a provider's prompt cache (`cachedInstructions` in
707
- * `@jigs.design/ai-sdk`).
721
+ * it, and the end of the prompt, for a provider's prompt cache
722
+ * (`cachedInstructions` in `@jigs.design/ai-sdk`).
708
723
  */
709
724
  promptPrefix: (mode?: PromptMode) => string;
710
725
  /** Changes whenever the compiler, the runtime or the host does. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigs.design/server",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "createJigsServer: checks and streams programs, runs procedures, and serves the sandbox frame.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,11 +29,11 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@jigs.design/compiler": "0.3.2",
33
- "@jigs.design/prompt": "0.3.2",
34
- "@jigs.design/host": "0.3.2",
35
- "@jigs.design/protocol": "0.3.2",
36
- "@jigs.design/runtime": "0.3.2"
32
+ "@jigs.design/compiler": "0.3.4",
33
+ "@jigs.design/host": "0.3.4",
34
+ "@jigs.design/prompt": "0.3.4",
35
+ "@jigs.design/runtime": "0.3.4",
36
+ "@jigs.design/protocol": "0.3.4"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "zod": "^4.0.0"