spyret 0.1.0 → 0.2.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/README.md +78 -22
- package/THIRD_PARTY_NOTICES.txt +28 -1
- package/dist/spyret-browser.amd.js +61 -0
- package/dist/spyret-browser.d.mts +272 -0
- package/dist/spyret-browser.d.ts +272 -0
- package/dist/spyret-browser.global.js +57 -0
- package/dist/spyret-browser.js +57 -0
- package/dist/spyret-browser.mjs +57 -0
- package/dist/spyret.d.mts +29 -1
- package/dist/spyret.d.ts +29 -1
- package/dist/spyret.global.js +12 -6
- package/dist/spyret.js +12 -6
- package/dist/spyret.mjs +12 -6
- package/dist/spyret.pyret.js +66 -0
- package/docs/BROWSER_LIBRARY.md +97 -0
- package/docs/IDE_MIGRATION.md +38 -0
- package/docs/PYRET_CAPTURE.md +4 -2
- package/docs/RELEASING.md +18 -61
- package/docs/SPYTIAL_HOOKS.md +73 -0
- package/docs/SPYTIAL_RULES_REFERENCE.md +51 -0
- package/package.json +29 -8
- package/pyret/spytial.arr +527 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Spyret library and IDE ownership
|
|
2
|
+
|
|
3
|
+
Spyret now supplies the reusable browser integration. A Drive-imported native
|
|
4
|
+
module can install its own CPO display adapter; the IDE does **not** need a
|
|
5
|
+
Spytial-specific output branch. See [library setup](BROWSER_LIBRARY.md).
|
|
6
|
+
|
|
7
|
+
| Functionality | Owner |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Value capture, relationalization, reconstruction, `_spytial` collection | Spyret |
|
|
10
|
+
| Generated Pyret rule types, YAML section composition | Spyret |
|
|
11
|
+
| Pyret-facing diagram functions and runtime suspension/Stop handling | Spyret |
|
|
12
|
+
| Evaluator/layout setup, opaque handles, source preview and views | Spyret browser entry |
|
|
13
|
+
| CPO renderer compatibility code | Spyret, isolated in `registerCpoOutput` |
|
|
14
|
+
| Layout semantics, parsing, solving, graph controls and diagnostics | Core |
|
|
15
|
+
| Source selection, YAML source writeback, undo and keyboard shortcuts | IDE |
|
|
16
|
+
| Builtin aliases and package/asset delivery for existing programs | IDE build configuration |
|
|
17
|
+
|
|
18
|
+
In the separate companion IDE change, the `spytial` builtin becomes a small
|
|
19
|
+
forwarding wrapper around
|
|
20
|
+
`spyret/browser`. `spytial-rules` is a build-time copy of the generated Pyret
|
|
21
|
+
module. Its old `spytial-view` module and Spytial branch in `output-ui` are removed.
|
|
22
|
+
Existing `SP.diagram(value, yaml)` and `_output` methods continue to work; new
|
|
23
|
+
programs can use `SP.diagram(value)` and typed rules.
|
|
24
|
+
|
|
25
|
+
Stock CPO can instead load `spyret/pyret-module` through its existing `gdrive-js`
|
|
26
|
+
locator, optionally hidden behind one URL-imported `.arr` wrapper. No new native
|
|
27
|
+
URL locator or IDE plugin is required for that route. Drive hosting/access is a
|
|
28
|
+
separate deployment step.
|
|
29
|
+
|
|
30
|
+
The companion IDE change consumes a lockfile-verified vendored Spyret 0.2.0 package
|
|
31
|
+
archive, so this coordinated change builds without an unpublished registry
|
|
32
|
+
version or a sibling source checkout. Once 0.2.0 is published, replace that
|
|
33
|
+
archive dependency with the exact npm version and regenerate the copied assets;
|
|
34
|
+
no runtime code needs to change.
|
|
35
|
+
|
|
36
|
+
Future work should focus on an upstream public display-registration and
|
|
37
|
+
cancellation API, deployment/Drive-access testing, and optional source-editor
|
|
38
|
+
integration. The existing private CPO adapter remains a compatibility dependency.
|
package/docs/PYRET_CAPTURE.md
CHANGED
|
@@ -62,8 +62,10 @@ inspect its nominal token/display spelling.
|
|
|
62
62
|
|
|
63
63
|
No printer or arbitrary annotation runs during capture. Declared datatype methods
|
|
64
64
|
are behavior supplied by declarations and are outside the declared-slot state
|
|
65
|
-
contract; this includes `_output`.
|
|
66
|
-
|
|
65
|
+
contract; this includes `_output`. Callable `_spytial` metadata is also omitted
|
|
66
|
+
on ordinary objects and outside a datatype's declared slots; collecting layout
|
|
67
|
+
rules is a separate, explicit operation. Other non-method extension fields on
|
|
68
|
+
data values are rejected instead of dropped. Functions/methods in state-bearing slots,
|
|
67
69
|
opaque values, unrecognized branded library objects, standalone table Row
|
|
68
70
|
values, sparse arrays, unset/frozen references, and arbitrary reference
|
|
69
71
|
annotations produce explicit diagnostics. This API does not serialize closures
|
package/docs/RELEASING.md
CHANGED
|
@@ -1,69 +1,26 @@
|
|
|
1
|
-
# Releasing Spyret
|
|
1
|
+
# Releasing Spyret to Google Drive
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
declarations, with no Core runtime dependency. The host passes the returned
|
|
6
|
-
`IDataInstance` to Core for layout/rendering; compatibility tests use Core 6.3.1.
|
|
7
|
-
|
|
8
|
-
## One-time npm setup
|
|
9
|
-
|
|
10
|
-
The first package publication requires an npm maintainer login. From a checked,
|
|
11
|
-
merged release commit, run:
|
|
12
|
-
|
|
13
|
-
```sh
|
|
14
|
-
npm login
|
|
15
|
-
npm install --global npm@11.20.0
|
|
16
|
-
npm ci
|
|
17
|
-
npm run typecheck
|
|
18
|
-
npm test
|
|
19
|
-
npm run test:package -- --out release
|
|
20
|
-
npm publish ./release/spyret-0.1.0.tgz --access public
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Also require green upstream PBT CI for this commit before publishing. The package
|
|
24
|
-
check installs the actual tarball into an empty project and exercises
|
|
25
|
-
CJS, ESM, browser and TypeScript consumers.
|
|
26
|
-
|
|
27
|
-
After the package exists, configure its npm **Trusted Publisher** settings:
|
|
28
|
-
|
|
29
|
-
| Setting | Value |
|
|
30
|
-
| --- | --- |
|
|
31
|
-
| Provider | GitHub Actions |
|
|
32
|
-
| Organization/user | `sidprasad` |
|
|
33
|
-
| Repository | `spyret` |
|
|
34
|
-
| Workflow filename | `release.yml` |
|
|
35
|
-
| Environment | Leave empty |
|
|
36
|
-
| Allowed action | Enable direct `npm publish` |
|
|
37
|
-
|
|
38
|
-
With npm 11.15 or later, the equivalent command is:
|
|
3
|
+
With Node 22+, [GitHub CLI](https://cli.github.com/), and `tar` installed, run
|
|
4
|
+
from this checkout (no `npm install` or build needed):
|
|
39
5
|
|
|
40
6
|
```sh
|
|
41
|
-
npm
|
|
7
|
+
npm run release:drive
|
|
42
8
|
```
|
|
43
9
|
|
|
44
|
-
|
|
45
|
-
No npm token needs to be stored in GitHub. Publishing uses OIDC and provenance.
|
|
46
|
-
|
|
47
|
-
## Later releases
|
|
48
|
-
|
|
49
|
-
1. On a release branch, run `npm version patch --no-git-tag-version` (or choose a
|
|
50
|
-
minor/major bump), commit both manifests, and merge after CI passes.
|
|
51
|
-
2. On the updated `main`, tag that exact commit and push the tag:
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
git tag v0.1.1
|
|
55
|
-
git push origin v0.1.1
|
|
56
|
-
```
|
|
10
|
+
The script downloads the latest GitHub release and walks you through:
|
|
57
11
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
12
|
+
1. Upload the prepared `.js` file to your Google Drive. Set **Anyone with the
|
|
13
|
+
link / Viewer**, then paste its Drive link into the script.
|
|
14
|
+
2. Upload the generated `.arr` file with the same sharing setting and paste
|
|
15
|
+
its Drive link into the script.
|
|
16
|
+
3. Copy the printed import into CPO and run the provided diagram example.
|
|
17
|
+
Share that import line with users: it includes typed constructors and diagrams.
|
|
61
18
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
19
|
+
Keep both filenames unchanged and upload them as files without conversion.
|
|
20
|
+
The two upload files are in `release/vVERSION/upload/`; the CPO example is
|
|
21
|
+
saved separately as `release/vVERSION/import.arr`. Keep published Drive files
|
|
22
|
+
unchanged so existing imports continue to work.
|
|
66
23
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
24
|
+
To select a specific release, use `npm run release:drive -- --tag v0.2.0`.
|
|
25
|
+
The release must contain the browser module and Pyret rules; `v0.1.1` is headless
|
|
26
|
+
and cannot be used. No Google Cloud project, service account, or API setup is needed.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Layout hooks and typed rules
|
|
2
|
+
|
|
3
|
+
The package includes `pyret/spytial.arr`, a typed rule library generated from
|
|
4
|
+
Core's language manifest. Copy or serve that file where your Pyret program can
|
|
5
|
+
import it (Node hosts can locate it with `require.resolve('spyret/spytial.arr')`).
|
|
6
|
+
|
|
7
|
+
```pyret
|
|
8
|
+
import file("spytial.arr") as S
|
|
9
|
+
|
|
10
|
+
data Tree:
|
|
11
|
+
| leaf(value)
|
|
12
|
+
| branch(children)
|
|
13
|
+
sharing:
|
|
14
|
+
method _spytial(self) -> List<S.SpytialRule>:
|
|
15
|
+
[list:
|
|
16
|
+
S.orientation("children", [list: S.direction-below]),
|
|
17
|
+
S.align("siblings", S.alignment-horizontal)
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Public constructors wrap their results as `constraint(Constraint)` or
|
|
24
|
+
`directive(Directive)` automatically. Compose rules with ordinary Pyret lists;
|
|
25
|
+
the serializer puts each rule in its proper YAML section. Optional fields and
|
|
26
|
+
style blocks have typed, immutable setters:
|
|
27
|
+
|
|
28
|
+
```pyret
|
|
29
|
+
S.atom-style-with(S.default-atom-style-options
|
|
30
|
+
.with-selector("leaf")
|
|
31
|
+
.with-fill-style(S.default-fill-style.with-color("red")))
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
From the JavaScript host, after evaluating the program:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { toDataInstance, getSpytialSpec } from 'spyret';
|
|
38
|
+
|
|
39
|
+
const instance = toDataInstance(pyretValue, runtime);
|
|
40
|
+
const specs = await getSpytialSpec(pyretValue, runtime); // string[]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The collector walks reachable values in breadth-first order, including lists,
|
|
44
|
+
objects, tuples, arrays, references, dictionaries and table cells. It handles
|
|
45
|
+
cycles and invokes each distinct `_spytial` function or method once per call,
|
|
46
|
+
with methods bound to their first encountered owner. Repeated hooks never stop
|
|
47
|
+
the traversal of an instance's children. Hooks should describe types, independent
|
|
48
|
+
of the particular instance; unobserved datatype variants cannot be discovered.
|
|
49
|
+
|
|
50
|
+
Each hook returns `List<SpytialRule>`, serialized as one YAML document. A raw YAML
|
|
51
|
+
string is also accepted as an escape hatch, unchanged and unvalidated. No hooks
|
|
52
|
+
yields `[]`; a hook returning an empty list yields one empty spec. Identical
|
|
53
|
+
documents from different hooks remain separate. Hook failures, malformed rules
|
|
54
|
+
and unsupported values reject the promise with a `SpytialSpecError` and path.
|
|
55
|
+
|
|
56
|
+
Hooks execute user code. Call the async collector while its owning runtime is
|
|
57
|
+
idle or paused; a native Pyret module can use `runtime.pauseStack` around the
|
|
58
|
+
promise. `spytialRulesToYaml(rules, runtime)` also serializes an already evaluated
|
|
59
|
+
rule list directly. Capture and `toDataInstance` never invoke hooks, and omit
|
|
60
|
+
callable `_spytial` metadata on ordinary objects or outside a datatype's declared
|
|
61
|
+
slots. Other capture restrictions still apply.
|
|
62
|
+
|
|
63
|
+
See the [generated rule reference](SPYTIAL_RULES_REFERENCE.md) for every
|
|
64
|
+
constructor, enum and option. Pyret annotations check field types; serialization
|
|
65
|
+
checks numeric bounds, string patterns and incompatible directions. Selector
|
|
66
|
+
meaning and result arity remain Core's responsibility.
|
|
67
|
+
|
|
68
|
+
To update the API, pin the desired Core release in `package.json` and the lockfile,
|
|
69
|
+
then run `npm run generate:spytial`. Commit the generated Pyret source, serializer
|
|
70
|
+
schema and reference together. `npm run check:spytial` and the tests detect drift;
|
|
71
|
+
unknown field types or enum vocabularies fail generation for explicit handling.
|
|
72
|
+
No Core runtime dependency is added to the published package.
|
|
73
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Generated Pyret rule reference
|
|
2
|
+
|
|
3
|
+
Core 6.3.2; language 2026-09-18.
|
|
4
|
+
|
|
5
|
+
Import `pyret/spytial.arr` as `S`. Constructors return `S.SpytialRule` automatically.
|
|
6
|
+
Optional fields use typed options. Start with `S.default-<rule>-options`,
|
|
7
|
+
chain `.with-<field>(value)`, then pass it to `<rule>-with` after the required arguments.
|
|
8
|
+
Style blocks similarly offer `default-<block>` and `.with-<field>(value)`.
|
|
9
|
+
|
|
10
|
+
Numeric bounds, patterns and incompatible direction combinations are checked during serialization.
|
|
11
|
+
|
|
12
|
+
| Constructor | Optional fields |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `orientation(selector :: String, directions :: List<Direction>)` | `hold: Hold`, `source: RuleSource` |
|
|
15
|
+
| `cyclic(selector :: String)` | `direction: Rotation`, `hold: Hold`, `source: RuleSource` |
|
|
16
|
+
| `align(selector :: String, direction :: Alignment)` | `hold: Hold`, `source: RuleSource` |
|
|
17
|
+
| `group(selector :: String, name :: String)` | `add-edge: GroupAddEdge`, `show-label: Boolean`, `text-style: TextStyle`, `hold: Hold`, `source: RuleSource` |
|
|
18
|
+
| `size(width :: Number, height :: Number)` | `selector: String`, `source: RuleSource` |
|
|
19
|
+
| `hide-atom(selector :: String)` | `source: RuleSource` |
|
|
20
|
+
| `flag(name :: LayoutFlag)` | |
|
|
21
|
+
| `atom-style()` | `selector: String`, `fill-style: FillStyle`, `border-style: BorderStyle`, `icon-style: IconStyle`, `text-style: TextStyle`, `show-label: Boolean`, `source: RuleSource` |
|
|
22
|
+
| `edge-style(field :: String)` | `selector: String`, `filter: String`, `line-style: LineStyle`, `text-style: TextStyle`, `show-label: Boolean`, `hidden: Boolean`, `source: RuleSource` |
|
|
23
|
+
| `attribute(field :: String)` | `selector: String`, `filter: String`, `text-style: TextStyle`, `source: RuleSource` |
|
|
24
|
+
| `tag(to-tag :: String, name :: String, value :: String)` | `text-style: TextStyle`, `source: RuleSource` |
|
|
25
|
+
| `hide-field(field :: String)` | `selector: String`, `filter: String`, `source: RuleSource` |
|
|
26
|
+
| `inferred-edge(name :: String, selector :: String)` | `draw: String`, `line-style: LineStyle`, `text-style: TextStyle`, `color: String`, `style: LinePattern`, `weight: Number`, `highlight: String`, `source: RuleSource` |
|
|
27
|
+
| `icon(selector :: String, path :: String)` (deprecated; use atom-style) | `show-labels: Boolean`, `source: RuleSource` |
|
|
28
|
+
| `atom-color(value :: String, selector :: String)` (deprecated; use atom-style) | `source: RuleSource` |
|
|
29
|
+
| `edge-color(field :: String, value :: String)` (deprecated; use edge-style) | `selector: String`, `filter: String`, `style: LinePattern`, `weight: Number`, `highlight: String`, `show-label: Boolean`, `hidden: Boolean`, `source: RuleSource` |
|
|
30
|
+
|
|
31
|
+
## Enum values
|
|
32
|
+
|
|
33
|
+
- `TextSize`: `text-size-small`, `text-size-normal`, `text-size-large`
|
|
34
|
+
- `LinePattern`: `line-pattern-solid`, `line-pattern-dashed`, `line-pattern-dotted`
|
|
35
|
+
- `IconPlacement`: `icon-placement-full`, `icon-placement-badge`
|
|
36
|
+
- `Direction`: `direction-above`, `direction-below`, `direction-left`, `direction-right`, `direction-directly-above`, `direction-directly-below`, `direction-directly-left`, `direction-directly-right`
|
|
37
|
+
- `Hold`: `hold-always`, `hold-never`
|
|
38
|
+
- `Rotation`: `rotation-clockwise`, `rotation-counterclockwise`
|
|
39
|
+
- `Alignment`: `alignment-horizontal`, `alignment-vertical`
|
|
40
|
+
- `GroupEdgeDirection`: `group-edge-direction-none`, `group-edge-direction-togroup`, `group-edge-direction-fromgroup`
|
|
41
|
+
- `LayoutFlag`: `layout-flag-hide-disconnected`, `layout-flag-hide-disconnected-built-ins`
|
|
42
|
+
|
|
43
|
+
## Blocks
|
|
44
|
+
|
|
45
|
+
- `text-style(size: Option<TextSize>, color: Option<String>)`
|
|
46
|
+
- `line-style(color: Option<String>, pattern: Option<LinePattern>, weight: Option<Number>, highlight: Option<String>)`
|
|
47
|
+
- `fill-style(color: Option<String>)`
|
|
48
|
+
- `border-style(color: Option<String>, width: Option<Number>)`
|
|
49
|
+
- `icon-style(path: Option<String>, placement: Option<IconPlacement>, opacity: Option<Number>)`
|
|
50
|
+
- `rule-source(text: String, location: Option<String>)`
|
|
51
|
+
- `group-add-edge(points: Option<GroupEdgeDirection>, line-style: Option<LineStyle>, text-style: Option<TextStyle>)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spyret",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Headless interfaces between standard Pyret and Spytial",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Siddhartha Prasad",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
16
|
"docs",
|
|
17
|
-
"THIRD_PARTY_NOTICES.txt"
|
|
17
|
+
"THIRD_PARTY_NOTICES.txt",
|
|
18
|
+
"pyret"
|
|
18
19
|
],
|
|
19
20
|
"exports": {
|
|
20
21
|
".": {
|
|
@@ -27,31 +28,51 @@
|
|
|
27
28
|
"default": "./dist/spyret.js"
|
|
28
29
|
}
|
|
29
30
|
},
|
|
30
|
-
"./global": "./dist/spyret.global.js"
|
|
31
|
+
"./global": "./dist/spyret.global.js",
|
|
32
|
+
"./spytial.arr": "./pyret/spytial.arr",
|
|
33
|
+
"./browser": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/spyret-browser.d.mts",
|
|
36
|
+
"default": "./dist/spyret-browser.mjs"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/spyret-browser.d.ts",
|
|
40
|
+
"default": "./dist/spyret-browser.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"./browser/amd": "./dist/spyret-browser.amd.js",
|
|
44
|
+
"./pyret-module": "./dist/spyret.pyret.js"
|
|
31
45
|
},
|
|
32
46
|
"scripts": {
|
|
33
|
-
"build": "tsup && node scripts/write-notices.mjs",
|
|
47
|
+
"build": "npm run check:spytial && tsup && node scripts/write-pyret-module.mjs && node scripts/write-notices.mjs",
|
|
34
48
|
"typecheck": "tsc --noEmit",
|
|
35
49
|
"test": "vitest run",
|
|
36
50
|
"test:upstream": "node scripts/check-pyret-capture.mjs",
|
|
37
51
|
"test:program": "node scripts/check-pyret-capture-program.mjs",
|
|
38
52
|
"test:pbt": "node scripts/check-pyret-pbt.mjs",
|
|
39
53
|
"prepack": "npm run typecheck && npm run build",
|
|
40
|
-
"test:package": "npm run build && node scripts/check-package.mjs"
|
|
54
|
+
"test:package": "npm run build && node scripts/check-package.mjs",
|
|
55
|
+
"generate:spytial": "node scripts/generate-spytial.mjs",
|
|
56
|
+
"check:spytial": "node scripts/generate-spytial.mjs --check",
|
|
57
|
+
"test:spytial": "node scripts/check-spytial-program.mjs",
|
|
58
|
+
"make:drive-wrapper": "node scripts/make-drive-wrapper.mjs",
|
|
59
|
+
"release:drive": "node scripts/prepare-drive-release.mjs"
|
|
41
60
|
},
|
|
42
61
|
"engines": {
|
|
43
62
|
"node": ">=22"
|
|
44
63
|
},
|
|
45
64
|
"devDependencies": {
|
|
65
|
+
"@types/js-yaml": "4.0.9",
|
|
46
66
|
"@types/node": "^24.0.3",
|
|
47
67
|
"fast-check": "^4.6.0",
|
|
68
|
+
"graphlib": "^2.1.8",
|
|
69
|
+
"js-yaml": "4.1.0",
|
|
48
70
|
"jsdom": "^26.1.0",
|
|
71
|
+
"spytial-core": "6.3.2",
|
|
49
72
|
"tsup": "^8.5.0",
|
|
50
73
|
"tsx": "^4.21.0",
|
|
51
74
|
"typescript": "^5.8.3",
|
|
52
|
-
"vitest": "^3.2.4"
|
|
53
|
-
"spytial-core": "6.3.1",
|
|
54
|
-
"graphlib": "^2.1.8"
|
|
75
|
+
"vitest": "^3.2.4"
|
|
55
76
|
},
|
|
56
77
|
"publishConfig": {
|
|
57
78
|
"access": "public",
|