@jarenjs/studio 0.34.2 → 0.43.3

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/README.md CHANGED
@@ -25,7 +25,8 @@ This package ships in two layers, the suite's convention:
25
25
  host registers the DOM-touching islands — the live nested-app stage
26
26
  (hot-update via `app.setState`) and the drag splitter — which are
27
27
  browser-verified. Mounted live at the website's `#/project`, where `app`
28
- files boot, `jslt`/`query` files run against a data file, and files are
28
+ files boot, `jslt`/`query` files run against a data file, `contract`
29
+ files render their `describe()`/OpenAPI projections, and files are
29
30
  added, renamed, deleted and opened from templates.
30
31
 
31
32
  ## The project document
@@ -14,7 +14,10 @@
14
14
  * HOT-DISPATCH into a running app (no reboot, the user keeps scroll and
15
15
  * inputs), while a `view`/`actions` change must reboot. It compares a
16
16
  * STRUCTURAL key (an app doc minus its `state`) via the suite's own
17
- * `contentKey`, so the policy in the widget reads clean, tested data.
17
+ * `semanticKey` the collision-free identity, not the memo-grade
18
+ * `contentKey` fingerprint: a classification decides whether a running
19
+ * app reboots, so a fingerprint collision would read a changed document
20
+ * as unchanged. The artifacts are parsed JSON, so the identity is total.
18
21
  */
19
22
  /**
20
23
  * Compose the project's files into runnable artifacts (whole-document).
@@ -65,4 +65,5 @@ export declare const KIND_BADGE: Readonly<{
65
65
  model: "model";
66
66
  fsm: "flow";
67
67
  dag: "flow";
68
+ contract: "model";
68
69
  }>;
@@ -12,6 +12,9 @@
12
12
  * real error comes back as its own coded `JQ`/`JT` code with
13
13
  * a docPath — the closed grammar would reject the operators;
14
14
  * - `fsm` / `dag` / `model` → their published grammar (structural);
15
+ * - `contract` → COMPILED by `compileContract`, so a refusal comes back
16
+ * as its stable `JC00xx` code with the docPath of the
17
+ * member at fault — richer than the grammar alone;
15
18
  * - `schema` → compiled as a JSON Schema (is it well-formed?);
16
19
  * - `state` / `data` → any JSON (structural only).
17
20
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/studio",
3
3
  "private": false,
4
- "version": "0.34.2",
4
+ "version": "0.43.3",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -59,11 +59,12 @@
59
59
  "prepack": "npm run build:types"
60
60
  },
61
61
  "dependencies": {
62
- "@jarenjs/core": "^0.34.2",
63
- "@jarenjs/validate": "^0.34.2",
64
- "@jarenjs/json": "^0.34.2",
65
- "@jarenjs/app": "^0.34.2",
66
- "@jarenjs/flow": "^0.34.2",
67
- "@jarenjs/db": "^0.34.2"
62
+ "@jarenjs/core": "^0.43.3",
63
+ "@jarenjs/validate": "^0.43.3",
64
+ "@jarenjs/json": "^0.43.3",
65
+ "@jarenjs/app": "^0.43.3",
66
+ "@jarenjs/flow": "^0.43.3",
67
+ "@jarenjs/db": "^0.43.3",
68
+ "@jarenjs/contract": "^0.43.3"
68
69
  }
69
70
  }
@@ -28,7 +28,7 @@
28
28
  "name": { "type": "string", "minLength": 1 },
29
29
  "kind": {
30
30
  "description": "The file's grammar/role; its `text` is validated against this kind, not the envelope.",
31
- "enum": ["app", "jslt", "query", "state", "data", "schema", "fsm", "dag", "model"]
31
+ "enum": ["app", "jslt", "query", "state", "data", "schema", "fsm", "dag", "model", "contract"]
32
32
  },
33
33
  "text": { "type": "string" }
34
34
  }
package/src/assemble.js CHANGED
@@ -15,20 +15,24 @@
15
15
  * HOT-DISPATCH into a running app (no reboot, the user keeps scroll and
16
16
  * inputs), while a `view`/`actions` change must reboot. It compares a
17
17
  * STRUCTURAL key (an app doc minus its `state`) via the suite's own
18
- * `contentKey`, so the policy in the widget reads clean, tested data.
18
+ * `semanticKey` the collision-free identity, not the memo-grade
19
+ * `contentKey` fingerprint: a classification decides whether a running
20
+ * app reboots, so a fingerprint collision would read a changed document
21
+ * as unchanged. The artifacts are parsed JSON, so the identity is total.
19
22
  */
20
23
 
21
- import { contentKey } from '@jarenjs/core/object';
24
+ import { semanticKey } from '@jarenjs/core/object';
22
25
  import { validateFile } from './validate.js';
23
26
 
24
27
  /** Kinds that are runnable artifacts on their own (vs. `state`/`data`
25
28
  * inputs). */
26
- const RUNNABLE = new Set(['app', 'jslt', 'query', 'schema', 'fsm', 'dag', 'model']);
29
+ const RUNNABLE = new Set(['app', 'jslt', 'query', 'schema', 'fsm', 'dag', 'model', 'contract']);
27
30
 
28
31
  /** A human role per kind, for the file rail's legibility. */
29
32
  const ROLE = Object.freeze({
30
33
  app: 'application', jslt: 'transform', query: 'query', state: 'state',
31
34
  data: 'data', schema: 'schema', fsm: 'state machine', dag: 'dataflow', model: 'data store',
35
+ contract: 'contract',
32
36
  });
33
37
 
34
38
  /**
@@ -59,8 +63,8 @@ export function assembleArtifacts(project) {
59
63
  * (its `state` excluded); everything else in full. */
60
64
  function structuralKey(artifact) {
61
65
  return artifact.kind === 'app'
62
- ? contentKey({ view: artifact.doc.view, actions: artifact.doc.actions, subs: artifact.doc.subs })
63
- : contentKey(artifact.doc);
66
+ ? semanticKey({ view: artifact.doc.view, actions: artifact.doc.actions, subs: artifact.doc.subs })
67
+ : semanticKey(artifact.doc);
64
68
  }
65
69
 
66
70
  const RANK = { none: 0, 'state-only': 1, structural: 2 };
@@ -88,7 +92,7 @@ export function classifyChange(prevProject, nextProject) {
88
92
  let c;
89
93
  if (b === undefined) c = 'structural';
90
94
  else if (structuralKey(b) !== structuralKey(a)) c = 'structural';
91
- else if (contentKey(b.doc) !== contentKey(a.doc)) c = 'state-only';
95
+ else if (semanticKey(b.doc) !== semanticKey(a.doc)) c = 'state-only';
92
96
  else c = 'none';
93
97
  perArtifact[name] = c;
94
98
  bump(c);
@@ -56,4 +56,7 @@ export function errorLine(content) {
56
56
  export const KIND_BADGE = Object.freeze({
57
57
  app: 'view', jslt: 'view', query: 'query', state: 'json',
58
58
  data: 'json', schema: 'json', model: 'model', fsm: 'flow', dag: 'flow',
59
+ // the model family: documents that declare a schema-backed surface —
60
+ // the badge palette is a closed five-hue set (studio.css header)
61
+ contract: 'model',
59
62
  });
@@ -87,6 +87,7 @@ const shell = {
87
87
  ['option', { value: 'state' }, 'state'],
88
88
  ['option', { value: 'data' }, 'data'],
89
89
  ['option', { value: 'schema' }, 'schema'],
90
+ ['option', { value: 'contract' }, 'contract'],
90
91
  ],
91
92
  [{ $apply: '$.rail[*]' }]],
92
93
  // ——— editor ———
@@ -37,10 +37,12 @@ function deriveStage(project, activeMeta, results, revision, committed) {
37
37
  // here it is the data: the assembled document + the reboot revision
38
38
  return { kind: 'app', mount: { doc: artifact.doc, revision } };
39
39
  }
40
- if (activeMeta.kind === 'jslt' || activeMeta.kind === 'query' || activeMeta.kind === 'schema') {
41
- // the host runs the file (a transform, or a schema validating the data
42
- // file) and stores its render nodes; the stage renders them in `ui`
43
- // mode, or a hint until the first run lands
40
+ if (activeMeta.kind === 'jslt' || activeMeta.kind === 'query' || activeMeta.kind === 'schema'
41
+ || activeMeta.kind === 'contract') {
42
+ // the host runs the file (a transform, a schema validating the data
43
+ // file, or a contract's describe()/OpenAPI projections) and stores
44
+ // its render nodes; the stage renders them in `ui` mode, or a hint
45
+ // until the first run lands
44
46
  const result = results[activeMeta.name] ?? null;
45
47
  return { kind: 'result', ran: result !== null, nodes: result?.nodes ?? [] };
46
48
  }
package/src/project.js CHANGED
@@ -15,6 +15,7 @@ import { StudioError } from './errors.js';
15
15
  /** The closed set of file kinds (matches the schema `kind` enum). */
16
16
  export const KINDS = Object.freeze([
17
17
  'app', 'jslt', 'query', 'state', 'data', 'schema', 'fsm', 'dag', 'model',
18
+ 'contract',
18
19
  ]);
19
20
 
20
21
  /** The default IDE layout — the frozen `{ mode, ratio, autorun }` shape
package/src/validate.js CHANGED
@@ -13,6 +13,9 @@
13
13
  * real error comes back as its own coded `JQ`/`JT` code with
14
14
  * a docPath — the closed grammar would reject the operators;
15
15
  * - `fsm` / `dag` / `model` → their published grammar (structural);
16
+ * - `contract` → COMPILED by `compileContract`, so a refusal comes back
17
+ * as its stable `JC00xx` code with the docPath of the
18
+ * member at fault — richer than the grammar alone;
16
19
  * - `schema` → compiled as a JSON Schema (is it well-formed?);
17
20
  * - `state` / `data` → any JSON (structural only).
18
21
  *
@@ -27,6 +30,7 @@ import { compileJsonQuery } from '@jarenjs/json';
27
30
  import {
28
31
  compileJsltStylesheet, createJsltRegistry, mathPack, financePack, statsPack,
29
32
  } from '@jarenjs/json/jslt';
33
+ import { compileContract } from '@jarenjs/contract';
30
34
 
31
35
  import appSchema from '@jarenjs/app/schemas/jaren-app.schema.json' with { type: 'json' };
32
36
  import querySchema from '@jarenjs/json/schemas/jaren-query.schema.json' with { type: 'json' };
@@ -178,6 +182,8 @@ function validateFileUncached(file, options = {}) {
178
182
  return schemaResult(kind, validateDag(doc));
179
183
  case 'model':
180
184
  return schemaResult(kind, validateModel(doc));
185
+ case 'contract':
186
+ return compileResult(kind, () => compileContract(doc));
181
187
  case 'schema':
182
188
  // a JSON Schema is a boolean or an object; anything else is not a
183
189
  // schema at all (the compiler is otherwise lenient about a schema's