@loom-forge/runtime 0.1.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 +101 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @loom-forge/runtime
|
|
2
|
+
|
|
3
|
+
**O motor montado.** Um install, uma versão de cada peça, e uma montagem — a que todo app React
|
|
4
|
+
sobre o loom-forge faz igual. Este package **não tem algoritmo**: o que ele traz é o conjunto e o
|
|
5
|
+
default.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @loom-forge/runtime react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { createRuntime, ForgeProvider, ForgeView } from "@loom-forge/runtime";
|
|
13
|
+
|
|
14
|
+
const runtime = createRuntime({
|
|
15
|
+
registry: [
|
|
16
|
+
["Card", Card, { schema }],
|
|
17
|
+
["Field", Field, fieldDescriptor],
|
|
18
|
+
],
|
|
19
|
+
defs: [userCard], // os componentes declarados em JSON
|
|
20
|
+
actions: port, // o `ActionPort` do host (o Tier 0 do contrato de actions)
|
|
21
|
+
onActionResult: (id, result) => {
|
|
22
|
+
if (!result.success) toast(result.error?.code);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
<ForgeProvider forge={runtime}>
|
|
27
|
+
<ForgeView node={page} data={data} contexts={contexts} />
|
|
28
|
+
</ForgeProvider>;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Por que ele existe
|
|
34
|
+
|
|
35
|
+
Não é o número de `install`. São duas coisas:
|
|
36
|
+
|
|
37
|
+
**Uma fronteira de dependência que nenhuma das duas pontas pode cruzar.** O
|
|
38
|
+
[`forge-react`](../forge-react/README.md) não pode depender do
|
|
39
|
+
[`tslite`](../tslite/README.md) — arrastaria a linguagem inteira para quem trocou o binder, e o
|
|
40
|
+
port `BindingEngine`, que existe para o binder ser trocável, viraria enfeite. E o `tslite` não
|
|
41
|
+
pode depender do `forge-react`: ele é o adapter da linguagem, e não conhece view. O conjunto só
|
|
42
|
+
pode ser um **terceiro**.
|
|
43
|
+
|
|
44
|
+
**E uma falha silenciosa.** O default do compositor é o `identityBinder`. Um motor montado sem o
|
|
45
|
+
binder do TSLite trata `{{ props.x }}` como a **string** `"{{ props.x }}"`: nada lança, nada
|
|
46
|
+
avisa, e a tela mostra a chave. Há um teste aqui que crava os dois lados — o mesmo documento, com
|
|
47
|
+
e sem a montagem.
|
|
48
|
+
|
|
49
|
+
## `createRuntime(options)`
|
|
50
|
+
|
|
51
|
+
Devolve o `Forge` do [`forge-react`](../forge-react/README.md), montado. É o `createForge` com a
|
|
52
|
+
montagem decidida, não um motor diferente — tudo tem override.
|
|
53
|
+
|
|
54
|
+
| opção | default | o quê |
|
|
55
|
+
| ------------------------------ | ---------------------- | --------------------------------------------------------- |
|
|
56
|
+
| `registry` | — | as peças: `[type, componente, descriptor?]` |
|
|
57
|
+
| `defs` | — | os componentes em JSON; entram **compilados**, uma vez |
|
|
58
|
+
| `actions` | — | o `ActionPort` do host |
|
|
59
|
+
| `onActionResult` | aviso em dev na falha | recebe **todo** envelope que voltou |
|
|
60
|
+
| `binder` | `createTsliteBinder()` | a linguagem. Trocar o perfil é montar o binder e passá-lo |
|
|
61
|
+
| `planners` / `layoutRenderers` | os do compositor | mapas `name → planner`/`renderer` |
|
|
62
|
+
| e o resto do `createForge` | — | `resolver`, `capabilities`, `requireEntryRoot`, `inspect` |
|
|
63
|
+
|
|
64
|
+
> [!WARNING]
|
|
65
|
+
> **Passe o `onActionResult`.** O envelope tem dois consumidores: o documento, pela cadeia
|
|
66
|
+
> `then`/`catch` que ele declarou, e o **host**, sempre. Sem ouvinte, uma falha de domínio vira
|
|
67
|
+
> aviso em dev e desaparece em produção.
|
|
68
|
+
|
|
69
|
+
**O que NÃO entra na montagem:** `data`, `contexts` e `commands`. Os três são dado de **render** —
|
|
70
|
+
mudam com o mundo, e o lugar deles é o `<ForgeView>`. Congelá-los aqui fixaria dado num artefato
|
|
71
|
+
do relógio lento.
|
|
72
|
+
|
|
73
|
+
## O conjunto
|
|
74
|
+
|
|
75
|
+
| package | o quê |
|
|
76
|
+
| ----------------------------------------- | ----------------------------------------------- |
|
|
77
|
+
| [`core`](../core/README.md) | o kernel headless e os ports |
|
|
78
|
+
| [`react`](../react/README.md) | a view: registry de componentes e os renderers |
|
|
79
|
+
| [`tslite`](../tslite/README.md) | a linguagem: `{{ }}` compilado e o bind gateado |
|
|
80
|
+
| [`forge`](../forge/README.md) | o construtor: IR, grafo, regras, estado |
|
|
81
|
+
| [`forge-react`](../forge-react/README.md) | a view do construtor e o boundary de estado |
|
|
82
|
+
|
|
83
|
+
O modelo, a análise e a view saem **flat** deste package. Da linguagem sai o que se **monta** —
|
|
84
|
+
`createTsliteBinder`, `DEFAULT_PROFILE` —; a fase de compilação (`compile`, os diagnósticos, o
|
|
85
|
+
`addressOf` da árvore) fica no `@loom-forge/tslite`, que continua sendo um package: quem consolida
|
|
86
|
+
no servidor importa de lá e não carrega o executor.
|
|
87
|
+
|
|
88
|
+
**Fora do conjunto, e por motivos diferentes:**
|
|
89
|
+
|
|
90
|
+
- [`check`](../check/README.md) e [`language-service`](../language-service/README.md) — **outro
|
|
91
|
+
relógio**. Tipo é autoria e CI, nunca o hot path de render (ADR-206 do `check`), e eles arrastam
|
|
92
|
+
o `@tslite/checker`. Instale como `devDependency`;
|
|
93
|
+
- [`eject`](../eject/README.md) — traduz a definição para TSX; é ferramenta, não runtime;
|
|
94
|
+
- [`grid`](../grid/README.md) — tem peer próprio (`react-grid-layout`). Entra quando houver
|
|
95
|
+
dashboard: `planners`/`layoutRenderers` são opções do `createRuntime`;
|
|
96
|
+
- [`editor`](../editor/README.md) e [`address`](../address/README.md) — são de quem **edita** o
|
|
97
|
+
documento, e cada um tem peer próprio.
|
|
98
|
+
|
|
99
|
+
## Licença
|
|
100
|
+
|
|
101
|
+
MIT © Anderson D. Rosa
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CreateForgeOptions, Forge } from '@loom-forge/forge-react';
|
|
2
|
+
export * from '@loom-forge/forge-react';
|
|
3
|
+
import { ComponentType } from 'react';
|
|
4
|
+
import { EngineDescriptor } from '@loom-forge/react';
|
|
5
|
+
export * from '@loom-forge/react';
|
|
6
|
+
import { ComponentDef } from '@loom-forge/forge';
|
|
7
|
+
export * from '@loom-forge/forge';
|
|
8
|
+
export * from '@loom-forge/core';
|
|
9
|
+
export { DEFAULT_PROFILE, TsliteBinderOptions, createTsliteBinder } from '@loom-forge/tslite';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Uma peça do kit, como o host a conhece: o `type` que o documento cita, o componente React, e
|
|
13
|
+
* o descriptor headless (`schema`, `meta.emits`, `layout`) — a forma que o `register` recebe.
|
|
14
|
+
*/
|
|
15
|
+
type RuntimePiece = readonly [
|
|
16
|
+
type: string,
|
|
17
|
+
component: ComponentType<never>,
|
|
18
|
+
descriptor?: EngineDescriptor
|
|
19
|
+
];
|
|
20
|
+
interface CreateRuntimeOptions extends CreateForgeOptions {
|
|
21
|
+
/** as peças que o documento pode citar por `type`. */
|
|
22
|
+
registry?: readonly RuntimePiece[];
|
|
23
|
+
/** os componentes declarados em JSON. Entram COMPILADOS, uma vez (o relógio lento). */
|
|
24
|
+
defs?: readonly ComponentDef[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* O motor pronto: o binder do TSLite, os planners e os renderers default, o boundary de estado,
|
|
28
|
+
* o kit registrado e as definições declaradas.
|
|
29
|
+
*
|
|
30
|
+
* Tudo tem override — é o `createForge` com a montagem decidida, não um motor diferente. Passar
|
|
31
|
+
* `binder` troca a linguagem inteira; para só mudar o perfil ou a stdlib, monte o binder e
|
|
32
|
+
* passe-o: `createRuntime({ binder: createTsliteBinder({ profile }) })`.
|
|
33
|
+
*
|
|
34
|
+
* **O que NÃO entra aqui:** `data`, `contexts` e `commands`. Os três são dado de RENDER — mudam
|
|
35
|
+
* com o mundo, e o lugar deles é o `<ForgeView>`. Congelá-los na montagem fixaria dado num
|
|
36
|
+
* artefato do relógio lento, que é a classe de bug que o motor inteiro evita.
|
|
37
|
+
*/
|
|
38
|
+
declare function createRuntime(opts?: CreateRuntimeOptions): Forge;
|
|
39
|
+
|
|
40
|
+
export { type CreateRuntimeOptions, type RuntimePiece, createRuntime };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createForge } from '@loom-forge/forge-react';
|
|
2
|
+
export * from '@loom-forge/forge-react';
|
|
3
|
+
import { createTsliteBinder } from '@loom-forge/tslite';
|
|
4
|
+
export { DEFAULT_PROFILE, createTsliteBinder } from '@loom-forge/tslite';
|
|
5
|
+
export * from '@loom-forge/core';
|
|
6
|
+
export * from '@loom-forge/forge';
|
|
7
|
+
export * from '@loom-forge/react';
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
function createRuntime(opts = {}) {
|
|
11
|
+
const { registry, defs, binder, ...rest } = opts;
|
|
12
|
+
const forge = createForge({
|
|
13
|
+
binder: binder ?? createTsliteBinder(),
|
|
14
|
+
...rest
|
|
15
|
+
});
|
|
16
|
+
for (const [type, component, descriptor] of registry ?? [])
|
|
17
|
+
forge.register(type, component, descriptor);
|
|
18
|
+
for (const def of defs ?? []) forge.defineComponent(def);
|
|
19
|
+
return forge;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { createRuntime };
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;AA2DO,SAAS,aAAA,CAAc,IAAA,GAA6B,EAAC,EAAU;AACpE,EAAA,MAAM,EAAE,QAAA,EAAU,IAAA,EAAM,MAAA,EAAQ,GAAG,MAAK,GAAI,IAAA;AAC5C,EAAA,MAAM,QAAQ,WAAA,CAAY;AAAA,IACxB,MAAA,EAAQ,UAAU,kBAAA,EAAmB;AAAA,IACrC,GAAG;AAAA,GACJ,CAAA;AACD,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,SAAA,EAAW,UAAU,CAAA,IAAK,YAAY,EAAC;AACvD,IAAA,KAAA,CAAM,QAAA,CAAS,IAAA,EAAM,SAAA,EAAqC,UAAU,CAAA;AACtE,EAAA,KAAA,MAAW,OAAO,IAAA,IAAQ,EAAC,EAAG,KAAA,CAAM,gBAAgB,GAAG,CAAA;AACvD,EAAA,OAAO,KAAA;AACT","file":"index.js","sourcesContent":["// ════════════════════════════════════════════════════════════════════════\n// O RUNTIME — o motor montado, e o único lugar onde a montagem é decidida.\n//\n// Este package não tem algoritmo nenhum. Ele existe por causa de uma fronteira\n// de DEPENDÊNCIA que nem o `forge-react` nem o `tslite` podem cruzar: o\n// `forge-react` não pode depender do `tslite` (arrastaria a linguagem inteira\n// para quem trocou o binder, e o port `BindingEngine` viraria enfeite), e o\n// `tslite` não pode depender do `forge-react` (ele é o adapter da linguagem, e\n// não conhece view). O conjunto só pode ser um TERCEIRO — este.\n//\n// E a razão de ele existir não é a conveniência de um `install`: sem binder, o\n// default do compositor é o `identityBinder`, e `{{ props.x }}` vira a STRING\n// `\"{{ props.x }}\"` na tela. Nada lança, nada avisa. Quem monta o motor à mão\n// erra isso uma vez e perde a tarde.\n//\n// O corte é a linha que este repo já usa: o que RODA em produção entra; o que\n// PROVA (`check`, `language-service`), o que traduz (`eject`) e o que edita\n// (`editor`, `address`) fica de fora, como o `grid`, que tem peer próprio.\n// ════════════════════════════════════════════════════════════════════════\n\nimport {\n createForge,\n type CreateForgeOptions,\n type Forge,\n} from \"@loom-forge/forge-react\";\nimport { createTsliteBinder } from \"@loom-forge/tslite\";\nimport type { ComponentType } from \"react\";\nimport type { EngineDescriptor } from \"@loom-forge/react\";\nimport type { ComponentDef } from \"@loom-forge/forge\";\n\n/**\n * Uma peça do kit, como o host a conhece: o `type` que o documento cita, o componente React, e\n * o descriptor headless (`schema`, `meta.emits`, `layout`) — a forma que o `register` recebe.\n */\nexport type RuntimePiece = readonly [\n type: string,\n component: ComponentType<never>,\n descriptor?: EngineDescriptor,\n];\n\nexport interface CreateRuntimeOptions extends CreateForgeOptions {\n /** as peças que o documento pode citar por `type`. */\n registry?: readonly RuntimePiece[];\n /** os componentes declarados em JSON. Entram COMPILADOS, uma vez (o relógio lento). */\n defs?: readonly ComponentDef[];\n}\n\n/**\n * O motor pronto: o binder do TSLite, os planners e os renderers default, o boundary de estado,\n * o kit registrado e as definições declaradas.\n *\n * Tudo tem override — é o `createForge` com a montagem decidida, não um motor diferente. Passar\n * `binder` troca a linguagem inteira; para só mudar o perfil ou a stdlib, monte o binder e\n * passe-o: `createRuntime({ binder: createTsliteBinder({ profile }) })`.\n *\n * **O que NÃO entra aqui:** `data`, `contexts` e `commands`. Os três são dado de RENDER — mudam\n * com o mundo, e o lugar deles é o `<ForgeView>`. Congelá-los na montagem fixaria dado num\n * artefato do relógio lento, que é a classe de bug que o motor inteiro evita.\n */\nexport function createRuntime(opts: CreateRuntimeOptions = {}): Forge {\n const { registry, defs, binder, ...rest } = opts;\n const forge = createForge({\n binder: binder ?? createTsliteBinder(),\n ...rest,\n });\n for (const [type, component, descriptor] of registry ?? [])\n forge.register(type, component as ComponentType<unknown>, descriptor);\n for (const def of defs ?? []) forge.defineComponent(def);\n return forge;\n}\n\n/* ── o conjunto ────────────────────────────────────────────────────────────────\n Re-exportado para que o host declare UM package e receba uma versão só de cada\n peça. O modelo, a análise e a view saem FLAT; da linguagem sai o que se monta\n (`createTsliteBinder`, `DEFAULT_PROFILE`), porque o resto dela — `compile`, o\n `addressOf` da árvore, os diagnósticos — é de quem consolida ou tipa, e o\n `addressOf` do `forge` (que também endereça o que mora FORA da árvore) é o que\n vale neste nível. Quem precisa da fase de compilação importa\n `@loom-forge/tslite` direto: ele continua sendo um package. */\n\nexport * from \"@loom-forge/core\";\nexport * from \"@loom-forge/forge\";\nexport * from \"@loom-forge/react\";\nexport * from \"@loom-forge/forge-react\";\nexport {\n createTsliteBinder,\n DEFAULT_PROFILE,\n type TsliteBinderOptions,\n} from \"@loom-forge/tslite\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loom-forge/runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "loom-forge · o RUNTIME de um app React: o motor montado com o binder do TSLite, os planners e os renderers default, e o boundary de estado. Um install, uma versão, uma montagem.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"loom-forge",
|
|
8
|
+
"runtime",
|
|
9
|
+
"json-ui",
|
|
10
|
+
"react",
|
|
11
|
+
"tslite"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Anderson D. Rosa",
|
|
16
|
+
"url": "https://github.com/andersondrosa"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/andersondrosa/loom-forge.git",
|
|
21
|
+
"directory": "packages/runtime"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/andersondrosa/loom-forge/tree/main/packages/runtime#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/andersondrosa/loom-forge/issues"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@loom-forge/core": "0.1.0",
|
|
42
|
+
"@loom-forge/forge-react": "0.1.0",
|
|
43
|
+
"@loom-forge/tslite": "0.1.0",
|
|
44
|
+
"@loom-forge/forge": "0.1.0",
|
|
45
|
+
"@loom-forge/react": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"react": "^18 || ^19"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/react": "^19.0.0",
|
|
52
|
+
"@types/react-dom": "^19.0.0",
|
|
53
|
+
"jsdom": "^29.1.1",
|
|
54
|
+
"react": "^19.0.0",
|
|
55
|
+
"react-dom": "^19.0.0",
|
|
56
|
+
"tsup": "^8.0.0",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup",
|
|
61
|
+
"dev": "tsup --watch",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"clean": "rm -rf dist"
|
|
66
|
+
}
|
|
67
|
+
}
|