@tai42/studio-sdk 9.2.1 → 10.0.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.
@@ -1,51 +1,37 @@
1
1
  /**
2
2
  * The string field renderer. A plain string renders as a text input (its
3
3
  * `type` derived from any JSON-Schema `format`); an expression-annotated schema
4
- * renders the jq field (a resting textarea with the visual-editor door); a
5
- * media-annotated schema renders the upload control instead; and when the form
6
- * supplies a completion provider, the input is backed by argument autocomplete.
4
+ * renders the INJECTED expression door (a resting textarea with the visual-editor
5
+ * button) when the host supplies one; a media-annotated schema renders the upload
6
+ * control instead; and when the form supplies a completion provider, the input is
7
+ * backed by argument autocomplete.
7
8
  *
8
- * `JqField` is reached through the SDK's own `./jq` re-export — the ONE
9
- * `@tai42/jq-studio` instance the whole deployment shares — never from
10
- * `@tai42/jq-studio` directly, which would bundle a second copy and orphan its
11
- * worker (see the `jq` module doc-comment).
12
- *
13
- * That door is loaded LAZILY, through a dynamic `import('../jq')` behind
14
- * {@link JqField}. The jq authoring door drags a heavy subgraph — the xyflow
15
- * visual editor, a Web Worker entry, and a wasm engine chunk — that a form
16
- * whose fields carry no `x-tai42-expression` annotation must never ship. A
17
- * static import would pin that subgraph into every consumer that bundles the
18
- * SDK (a host that externalises the SDK is unaffected either way); the dynamic
19
- * import keeps it out of `schema-form`'s static graph and pulls it only when an
20
- * annotated field actually mounts.
9
+ * NO JQ EDGE. This module — and every module under `schema-form`, and the package
10
+ * barrel above them — names no jq type and imports `@tai42/jq-studio` neither
11
+ * statically NOR dynamically.
12
+ * The jq authoring door drags a heavy subgraph (the xyflow visual editor, a Web
13
+ * Worker entry, and a multi-megabyte wasm engine), and a bundler EMITS that whole
14
+ * subgraph for a dynamic import just as it does for a static one — `lazy` defers
15
+ * the FETCH, never the emission. A consumer that bundles the SDK to render forms
16
+ * would still ship the chunks, the worker file, and the wasm it can never run.
17
+ * So the door arrives from the host instead, through
18
+ * {@link ExpressionFieldContext} (or the form's `expressionField` prop), and an
19
+ * annotated field with no injected door falls back to the plain string input.
21
20
  */
22
21
  import type { ReactNode } from 'react';
23
- import { Suspense, lazy, useCallback, useContext, useMemo, useState } from 'react';
22
+ import { Suspense, useCallback, useContext, useMemo, useState } from 'react';
24
23
 
25
24
  import { CompletionInput } from '../components/completion-input';
26
25
  import { Field } from '../components/field';
27
26
  import { AlertTriangleIcon } from '../components/icons';
28
27
  import { TextInput, Textarea } from '../components/inputs';
29
28
  import { errorMessage } from '../errors';
30
- import type { JqInputShapeDescriptor } from '../jq';
31
29
  import type { ExpressionAnnotation, MediaUpload } from './classify';
32
- import { CompletionProviderContext } from './context';
30
+ import type { ExpressionFieldComponent, ExpressionInputShape } from './context';
31
+ import { CompletionProviderContext, ExpressionFieldContext } from './context';
33
32
  import { MediaField } from './media-field';
34
33
  import type { CompletionProvider } from './SchemaForm';
35
34
 
36
- // The lazily-loaded jq door. Defining it at module scope (never inside a
37
- // component) is what keeps the lazy component's identity stable across renders,
38
- // so React resolves the chunk once and never re-suspends a mounted field. The
39
- // `import('../jq')` here is a DYNAMIC import — the sole runtime edge from
40
- // `schema-form` to the jq door — so the visual editor, worker, and wasm live in
41
- // a split chunk that a non-annotated form never requests. The `type`-only
42
- // import of `JqInputShapeDescriptor` above is erased at compile time and adds no
43
- // such edge.
44
- const JqField = lazy(async () => {
45
- const { JqField } = await import('../jq');
46
- return { default: JqField };
47
- });
48
-
49
35
  export function StringField({
50
36
  heading,
51
37
  description,
@@ -70,6 +56,7 @@ export function StringField({
70
56
  onChange: (value: unknown) => void;
71
57
  }): ReactNode {
72
58
  const completionProvider = useContext(CompletionProviderContext);
59
+ const expressionField = useContext(ExpressionFieldContext);
73
60
  const current = typeof value === 'string' ? value : '';
74
61
  // An empty optional field emits `undefined` (drops the key); a required one
75
62
  // keeps the empty string so validation can flag it.
@@ -77,15 +64,20 @@ export function StringField({
77
64
  onChange(next === '' && !required ? undefined : next);
78
65
  };
79
66
 
80
- // An expression-annotated field renders the jq field — the resting control plus
81
- // the visual-editor door. Checked before `media` because the expression
67
+ // An expression-annotated field renders the injected door — the resting control
68
+ // plus its visual-editor button. Checked before `media` because the expression
82
69
  // annotation is the more specific, deliberate opt-in (`x-tai42-expression`);
83
- // a schema carrying both is contradictory, and a jq expression is text, not an
70
+ // a schema carrying both is contradictory, and an expression is text, not an
84
71
  // upload. Not reachable together with the completion provider path: an
85
72
  // expression is authored, not completed from server suggestions.
86
- if (expression !== undefined) {
73
+ //
74
+ // With no door injected the annotation is INERT: the field falls through to the
75
+ // ordinary paths below, exactly as a malformed annotation does, so a form
76
+ // renders the same bytes either way.
77
+ if (expression !== undefined && expressionField !== undefined) {
87
78
  return (
88
- <JqExpressionField
79
+ <ExpressionField
80
+ component={expressionField}
89
81
  heading={heading}
90
82
  description={description}
91
83
  error={error}
@@ -198,29 +190,31 @@ function CompletionField({
198
190
  }
199
191
 
200
192
  /**
201
- * An expression-annotated string field, rendered as `JqField`: a multiline
202
- * resting control with the always-present visual-editor door. `JqField` brings
203
- * its own label/description/error chrome (a11y-linked slots), so it is NOT
204
- * wrapped in the form's `Field` component — the annotation's descriptor fields
205
- * map onto jq-studio's input-shape descriptor, and the form's per-field error
206
- * feeds the `error` prop.
193
+ * An expression-annotated string field, rendered through the injected door: a
194
+ * multiline resting control with the door's own visual-editor button. The door
195
+ * brings its own label/description/error chrome (a11y-linked slots), so it is NOT
196
+ * wrapped in the form's `Field` component — the annotation's descriptor fields map
197
+ * onto the input-shape descriptor, and the form's per-field error feeds `error`.
198
+ *
199
+ * The door is rendered as an ELEMENT, never called as a function, so its hooks and
200
+ * state (editor open, worker handle) belong to it and survive re-renders of this
201
+ * field.
207
202
  *
208
- * Deliberately unwired:
209
- * - `serverValidate` — no author-time validation endpoint applies to a
210
- * schema-declared field (the WASM runtime still powers the Test panel locally).
211
- * - `onEditorOpenChange` — the hosts that render `SchemaForm` register no global
212
- * keyboard shortcuts, so there is nothing to mute while the editor is open.
213
- * - `compact` — the form renders every sibling field with a visible label;
214
- * the compact variant (visually-hidden label, icon-only door) is for dense
215
- * host rows, which the schema form does not have.
203
+ * It mounts inside a `Suspense` boundary because a host is free to inject a `lazy`
204
+ * door to keep the jq subgraph in a split chunk of ITS OWN bundle. The fallback
205
+ * paints the resting shell — label/description/error chrome around the current
206
+ * value in a read-only multiline control — so the field holds its layout and keeps
207
+ * the value visible while that chunk resolves, and the swap does not flash. A door
208
+ * injected eagerly never suspends and renders straight through.
216
209
  *
217
- * The door is code-split ({@link JqField} is a `lazy` component), so it mounts
218
- * inside a `Suspense` boundary. Its fallback paints the resting shell —
219
- * label/description/error chrome around the current value in a read-only
220
- * multiline control — so the field holds its layout and keeps the value visible
221
- * while the chunk resolves, and the swap to the live door does not flash.
210
+ * The form passes only the props the field owns; a jq door's `serverValidate`,
211
+ * `onEditorOpenChange`, and `compact` stay unwired, because no author-time
212
+ * validation endpoint applies to a schema-declared field, the hosts that render
213
+ * `SchemaForm` register no global shortcuts to mute, and every sibling field shows
214
+ * a visible label (the compact variant is for dense host rows).
222
215
  */
223
- function JqExpressionField({
216
+ function ExpressionField({
217
+ component: Door,
224
218
  heading,
225
219
  description,
226
220
  error,
@@ -229,6 +223,7 @@ function JqExpressionField({
229
223
  value,
230
224
  onChange,
231
225
  }: {
226
+ component: ExpressionFieldComponent;
232
227
  heading: string;
233
228
  description: string | undefined;
234
229
  error: string | undefined;
@@ -237,13 +232,13 @@ function JqExpressionField({
237
232
  value: string;
238
233
  onChange: (value: string) => void;
239
234
  }): ReactNode {
240
- // Memoised: the descriptor's identity feeds jq-studio's own memoisation, so a
235
+ // Memoised: the descriptor's identity feeds the door's own memoisation, so a
241
236
  // fresh object each keystroke would defeat it.
242
237
  const shape = useMemo(() => expressionShape(expression, argName), [expression, argName]);
243
238
  return (
244
239
  <Suspense
245
240
  fallback={
246
- <JqExpressionFieldSkeleton
241
+ <ExpressionFieldSkeleton
247
242
  heading={heading}
248
243
  description={description}
249
244
  error={error}
@@ -251,7 +246,7 @@ function JqExpressionField({
251
246
  />
252
247
  }
253
248
  >
254
- <JqField
249
+ <Door
255
250
  label={heading}
256
251
  description={description}
257
252
  error={error}
@@ -265,13 +260,13 @@ function JqExpressionField({
265
260
  }
266
261
 
267
262
  /**
268
- * The resting shell shown while the jq door chunk loads. It mirrors the live
269
- * door's footprint — the same label/description/error chrome wrapping a
263
+ * The resting shell shown while a lazily-injected door resolves. It mirrors the
264
+ * live door's footprint — the same label/description/error chrome wrapping a
270
265
  * multiline control seeded with the current value — but the control is inert
271
266
  * (read-only, `aria-busy`) because there is nothing to edit yet. Matching the
272
267
  * footprint is what keeps the Suspense swap from shifting layout or flashing.
273
268
  */
274
- function JqExpressionFieldSkeleton({
269
+ function ExpressionFieldSkeleton({
275
270
  heading,
276
271
  description,
277
272
  error,
@@ -290,17 +285,16 @@ function JqExpressionFieldSkeleton({
290
285
  }
291
286
 
292
287
  /**
293
- * Map a classified expression annotation onto jq-studio's input-shape
294
- * descriptor. A bare annotation (`language` only) maps to NO descriptor — the
295
- * undeclared-shape `JqField` is the honest rendering when the server said
296
- * nothing about `.`. When any descriptor field is present, the required
297
- * descriptor members the annotation omits fall back to neutral values rather
298
- * than invented copy.
288
+ * Map a classified expression annotation onto the door's input-shape descriptor.
289
+ * A bare annotation (`language` only) maps to NO descriptor — an undeclared shape
290
+ * is the honest rendering when the server said nothing about `.`. When any
291
+ * descriptor field is present, the required descriptor members the annotation
292
+ * omits fall back to neutral values rather than invented copy.
299
293
  */
300
294
  function expressionShape(
301
295
  expression: ExpressionAnnotation,
302
296
  argName: string,
303
- ): JqInputShapeDescriptor | undefined {
297
+ ): ExpressionInputShape | undefined {
304
298
  const declared =
305
299
  expression.label !== undefined ||
306
300
  expression.blurb !== undefined ||
package/dist/jq.d.ts DELETED
@@ -1,23 +0,0 @@
1
- /**
2
- * The SDK's jq surface — a THIN re-export of `@tai42/jq-studio`, the standalone,
3
- * embeddable visual jq editor. There is no tai42-owned jq component: the platform
4
- * owns zero jq code. This module exists only so a feature package (which depends
5
- * on `@tai42/studio-sdk` alone, never on `@tai42/jq-studio` directly) can reach
6
- * the drop-in `JqField`, the lower-level `JqEditorDialog`, the agnostic
7
- * field-declaration contract, and the runtime loader/worker helpers.
8
- *
9
- * BUNDLE + WORKER SHARING. A plugin bundle externalises `@tai42/studio-sdk`, so
10
- * the SDK — and the one copy of `@tai42/jq-studio` it carries — ships once for the
11
- * whole deployment. A plugin that renders `JqField` shares the host's single jq
12
- * runtime, its single primitives injection (see the host's `PrimitivesProvider`
13
- * wiring), and the single worker the host installs at boot
14
- * ({@link installDefaultJqWorker}); it never bundles its own.
15
- *
16
- * The host app injects the SDK's design-system primitives into jq-studio ONCE at
17
- * the root through jq-studio's `PrimitivesProvider` and installs the shared worker
18
- * there, so every `JqField` — host feature or plugin — paints in the SDK design
19
- * system and evaluates off the main thread with no per-site wiring.
20
- */
21
- export { JqField, JQEditorDialog, JqEditorDialog, preloadJq, installDefaultJqWorker, PrimitivesProvider, } from '@tai42/jq-studio';
22
- export type { JqFieldProps, JQEditorDialogProps, JqEditorDialogProps, ExpressionLanguage, JqInputKey, JqInputShapeDescriptor, SampleInputProvider, ServerValidationResult, ServerValidateHook, JqFieldDeclaration, Primitives, } from '@tai42/jq-studio';
23
- //# sourceMappingURL=jq.d.ts.map
package/dist/jq.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"jq.d.ts","sourceRoot":"","sources":["../src/jq.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,OAAO,EACP,cAAc,EACd,cAAc,EACd,SAAS,EACT,sBAAsB,EAWtB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAGlB,UAAU,GACX,MAAM,kBAAkB,CAAC"}
package/dist/jq.js DELETED
@@ -1,33 +0,0 @@
1
- /**
2
- * The SDK's jq surface — a THIN re-export of `@tai42/jq-studio`, the standalone,
3
- * embeddable visual jq editor. There is no tai42-owned jq component: the platform
4
- * owns zero jq code. This module exists only so a feature package (which depends
5
- * on `@tai42/studio-sdk` alone, never on `@tai42/jq-studio` directly) can reach
6
- * the drop-in `JqField`, the lower-level `JqEditorDialog`, the agnostic
7
- * field-declaration contract, and the runtime loader/worker helpers.
8
- *
9
- * BUNDLE + WORKER SHARING. A plugin bundle externalises `@tai42/studio-sdk`, so
10
- * the SDK — and the one copy of `@tai42/jq-studio` it carries — ships once for the
11
- * whole deployment. A plugin that renders `JqField` shares the host's single jq
12
- * runtime, its single primitives injection (see the host's `PrimitivesProvider`
13
- * wiring), and the single worker the host installs at boot
14
- * ({@link installDefaultJqWorker}); it never bundles its own.
15
- *
16
- * The host app injects the SDK's design-system primitives into jq-studio ONCE at
17
- * the root through jq-studio's `PrimitivesProvider` and installs the shared worker
18
- * there, so every `JqField` — host feature or plugin — paints in the SDK design
19
- * system and evaluates off the main thread with no per-site wiring.
20
- */
21
- export { JqField, JQEditorDialog, JqEditorDialog, preloadJq, installDefaultJqWorker,
22
- // The design-system injection point. jq-studio renders its editor chrome
23
- // through primitives it reads from a module-scoped React context; the host
24
- // mounts `PrimitivesProvider` ONCE at the root to substitute the SDK design
25
- // system. It is re-exported HERE — off the same `@tai42/jq-studio` instance
26
- // `JqField` comes from — so the host reaches it through the ONE shared SDK the
27
- // import map serves, and the provider's context is the SAME object `JqField`
28
- // reads. A host that imported `PrimitivesProvider` from `@tai42/jq-studio`
29
- // directly would bundle a SECOND jq-studio copy: its `createContext` would be a
30
- // different object, the injection would silently fall back to the built-ins,
31
- // and the shell would ship a duplicate (orphan) jq worker + wasm.
32
- PrimitivesProvider, } from '@tai42/jq-studio';
33
- //# sourceMappingURL=jq.js.map
package/dist/jq.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"jq.js","sourceRoot":"","sources":["../src/jq.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,OAAO,EACP,cAAc,EACd,cAAc,EACd,SAAS,EACT,sBAAsB;AACtB,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,2EAA2E;AAC3E,gFAAgF;AAChF,6EAA6E;AAC7E,kEAAkE;AAClE,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
package/src/jq.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * The SDK's jq surface — a THIN re-export of `@tai42/jq-studio`, the standalone,
3
- * embeddable visual jq editor. There is no tai42-owned jq component: the platform
4
- * owns zero jq code. This module exists only so a feature package (which depends
5
- * on `@tai42/studio-sdk` alone, never on `@tai42/jq-studio` directly) can reach
6
- * the drop-in `JqField`, the lower-level `JqEditorDialog`, the agnostic
7
- * field-declaration contract, and the runtime loader/worker helpers.
8
- *
9
- * BUNDLE + WORKER SHARING. A plugin bundle externalises `@tai42/studio-sdk`, so
10
- * the SDK — and the one copy of `@tai42/jq-studio` it carries — ships once for the
11
- * whole deployment. A plugin that renders `JqField` shares the host's single jq
12
- * runtime, its single primitives injection (see the host's `PrimitivesProvider`
13
- * wiring), and the single worker the host installs at boot
14
- * ({@link installDefaultJqWorker}); it never bundles its own.
15
- *
16
- * The host app injects the SDK's design-system primitives into jq-studio ONCE at
17
- * the root through jq-studio's `PrimitivesProvider` and installs the shared worker
18
- * there, so every `JqField` — host feature or plugin — paints in the SDK design
19
- * system and evaluates off the main thread with no per-site wiring.
20
- */
21
- export {
22
- JqField,
23
- JQEditorDialog,
24
- JqEditorDialog,
25
- preloadJq,
26
- installDefaultJqWorker,
27
- // The design-system injection point. jq-studio renders its editor chrome
28
- // through primitives it reads from a module-scoped React context; the host
29
- // mounts `PrimitivesProvider` ONCE at the root to substitute the SDK design
30
- // system. It is re-exported HERE — off the same `@tai42/jq-studio` instance
31
- // `JqField` comes from — so the host reaches it through the ONE shared SDK the
32
- // import map serves, and the provider's context is the SAME object `JqField`
33
- // reads. A host that imported `PrimitivesProvider` from `@tai42/jq-studio`
34
- // directly would bundle a SECOND jq-studio copy: its `createContext` would be a
35
- // different object, the injection would silently fall back to the built-ins,
36
- // and the shell would ship a duplicate (orphan) jq worker + wasm.
37
- PrimitivesProvider,
38
- } from '@tai42/jq-studio';
39
- export type {
40
- JqFieldProps,
41
- JQEditorDialogProps,
42
- JqEditorDialogProps,
43
- ExpressionLanguage,
44
- JqInputKey,
45
- JqInputShapeDescriptor,
46
- SampleInputProvider,
47
- ServerValidationResult,
48
- ServerValidateHook,
49
- JqFieldDeclaration,
50
- // The nine-primitive contract a host builds its injection map against (see
51
- // `PrimitivesProvider`).
52
- Primitives,
53
- } from '@tai42/jq-studio';