@kumikijs/compiler 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/LICENSE +202 -0
- package/README.md +34 -34
- package/dist/capabilities-Bdub5oAe.js +75 -0
- package/dist/index.d.ts +55 -5
- package/dist/index.js +389 -17
- package/dist/node.d.ts +11 -1
- package/dist/node.js +26 -2
- package/package.json +13 -23
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# @kumikijs/compiler
|
|
2
|
-
|
|
3
|
-
Kumiki compiler — lexer, parser, typechecker, and code generator. Part of [Kumiki](https://github.com/kage1020/Kumiki), an AI-first web framework language.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```sh
|
|
8
|
-
npm i @kumikijs/compiler
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { check, compile, lex, parse } from "@kumikijs/compiler";
|
|
15
|
-
|
|
16
|
-
// Type-check a .kumiki source (returns diagnostics)
|
|
17
|
-
const diagnostics = check(source);
|
|
18
|
-
|
|
19
|
-
// Compile a source into a runnable HTML app
|
|
20
|
-
const result = compile(source);
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
To inline the runtime bundle from disk in a Node environment, use the `./node` subpath:
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { compile } from "@kumikijs/compiler";
|
|
27
|
-
import { nodeRuntimeBundleReader } from "@kumikijs/compiler/node";
|
|
28
|
-
|
|
29
|
-
const result = compile(source, { bundle: true, readRuntimeBundle: nodeRuntimeBundleReader });
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## License
|
|
33
|
-
|
|
34
|
-
Apache-2.0
|
|
1
|
+
# @kumikijs/compiler
|
|
2
|
+
|
|
3
|
+
Kumiki compiler — lexer, parser, typechecker, and code generator. Part of [Kumiki](https://github.com/kage1020/Kumiki), an AI-first web framework language.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @kumikijs/compiler
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { check, compile, lex, parse } from "@kumikijs/compiler";
|
|
15
|
+
|
|
16
|
+
// Type-check a .kumiki source (returns diagnostics)
|
|
17
|
+
const diagnostics = check(source);
|
|
18
|
+
|
|
19
|
+
// Compile a source into a runnable HTML app
|
|
20
|
+
const result = compile(source);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To inline the runtime bundle from disk in a Node environment, use the `./node` subpath:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { compile } from "@kumikijs/compiler";
|
|
27
|
+
import { nodeRuntimeBundleReader } from "@kumikijs/compiler/node";
|
|
28
|
+
|
|
29
|
+
const result = compile(source, { bundle: true, readRuntimeBundle: nodeRuntimeBundleReader });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
Apache-2.0
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/capabilities.ts
|
|
2
|
+
/** Capabilities that may appear in `app.caps` without any manifest. */
|
|
3
|
+
const STANDARD_CAPABILITIES = new Set([
|
|
4
|
+
"http.get",
|
|
5
|
+
"http.post",
|
|
6
|
+
"http.put",
|
|
7
|
+
"http.patch",
|
|
8
|
+
"http.delete",
|
|
9
|
+
"storage.read",
|
|
10
|
+
"storage.write",
|
|
11
|
+
"session.read",
|
|
12
|
+
"session.write",
|
|
13
|
+
"indexed.read",
|
|
14
|
+
"indexed.write",
|
|
15
|
+
"nav.push",
|
|
16
|
+
"nav.replace",
|
|
17
|
+
"nav.back",
|
|
18
|
+
"clipboard.read",
|
|
19
|
+
"clipboard.write",
|
|
20
|
+
"notification.show",
|
|
21
|
+
"analytics.send",
|
|
22
|
+
"log.write",
|
|
23
|
+
"crypto.random",
|
|
24
|
+
"crypto.hash",
|
|
25
|
+
"media.camera",
|
|
26
|
+
"media.microphone",
|
|
27
|
+
"geo.read",
|
|
28
|
+
"socket.connect",
|
|
29
|
+
"socket.send"
|
|
30
|
+
]);
|
|
31
|
+
/** A capability name must look like `group.action` (lowercase, dot-separated). */
|
|
32
|
+
const CAP_NAME = /^[a-z][a-z0-9]*\.[a-z][a-z0-9-]*$/;
|
|
33
|
+
/**
|
|
34
|
+
* Validate a parsed `kumiki.caps.json` value. Accepts either bare strings or
|
|
35
|
+
* `{ name, description? }` objects in the `capabilities` array. Pure — the
|
|
36
|
+
* caller does the file read + JSON parse and reports the location.
|
|
37
|
+
*/
|
|
38
|
+
function parseCapabilityManifest(raw) {
|
|
39
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: "manifest must be a JSON object"
|
|
42
|
+
};
|
|
43
|
+
const caps = raw.capabilities;
|
|
44
|
+
if (!Array.isArray(caps)) return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: "\"capabilities\" must be an array"
|
|
47
|
+
};
|
|
48
|
+
const names = [];
|
|
49
|
+
for (let i = 0; i < caps.length; i++) {
|
|
50
|
+
const entry = caps[i];
|
|
51
|
+
const name = typeof entry === "string" ? entry : pickName(entry);
|
|
52
|
+
if (typeof name !== "string" || name.length === 0) return {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: `capabilities[${i}] must be a string or an object with a non-empty "name"`
|
|
55
|
+
};
|
|
56
|
+
if (!CAP_NAME.test(name)) return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: `capability "${name}" must look like "group.action" (lowercase, dot-separated)`
|
|
59
|
+
};
|
|
60
|
+
if (STANDARD_CAPABILITIES.has(name)) return {
|
|
61
|
+
ok: false,
|
|
62
|
+
error: `capability "${name}" is already a standard capability — remove it from the manifest`
|
|
63
|
+
};
|
|
64
|
+
names.push(name);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
manifest: { capabilities: names }
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function pickName(entry) {
|
|
72
|
+
if (typeof entry === "object" && entry !== null) return entry.name;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { parseCapabilityManifest as n, STANDARD_CAPABILITIES as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,16 @@ type Program = {
|
|
|
31
31
|
kind: "Program";
|
|
32
32
|
defs: Def[];
|
|
33
33
|
};
|
|
34
|
-
type Def = TypeDef | SlotDef | ReducerDef | TileDef | FnDef | EffectDef | AppDef | ThemeDef;
|
|
34
|
+
type Def = TypeDef | SlotDef | ReducerDef | TileDef | FnDef | EffectDef | AppDef | ThemeDef | MotionDef | TestDef;
|
|
35
|
+
type TestDef = {
|
|
36
|
+
kind: "TestDef";
|
|
37
|
+
name: string; /** `reducer-test` targets a reducer name; `tile-test` targets a tile name. */
|
|
38
|
+
testKind: "reducer-test" | "tile-test";
|
|
39
|
+
target: string; /** The `given = { ... }` record literal (interpreted, not codegen'd as-is). */
|
|
40
|
+
given: Expr; /** `expect = { slots, effects }` / `{ panic }` (record) for reducer-test; a tile expression for tile-test. */
|
|
41
|
+
expect: Expr | TileExpr;
|
|
42
|
+
pos: Pos;
|
|
43
|
+
};
|
|
35
44
|
type ThemeValue = string | number | {
|
|
36
45
|
[k: string]: ThemeValue;
|
|
37
46
|
};
|
|
@@ -43,6 +52,14 @@ type ThemeDef = {
|
|
|
43
52
|
};
|
|
44
53
|
pos: Pos;
|
|
45
54
|
};
|
|
55
|
+
type MotionDef = {
|
|
56
|
+
kind: "MotionDef";
|
|
57
|
+
name: string;
|
|
58
|
+
body: {
|
|
59
|
+
[k: string]: ThemeValue;
|
|
60
|
+
};
|
|
61
|
+
pos: Pos;
|
|
62
|
+
};
|
|
46
63
|
type TypeDef = {
|
|
47
64
|
kind: "TypeDef";
|
|
48
65
|
name: string;
|
|
@@ -168,6 +185,7 @@ type EventPattern = {
|
|
|
168
185
|
} | {
|
|
169
186
|
kind: "TimerEvent";
|
|
170
187
|
intervalMs: number;
|
|
188
|
+
name?: string;
|
|
171
189
|
pos: Pos;
|
|
172
190
|
} | {
|
|
173
191
|
kind: "LifecycleEvent";
|
|
@@ -190,6 +208,10 @@ type Statement = {
|
|
|
190
208
|
effect: string;
|
|
191
209
|
args: Expr[];
|
|
192
210
|
pos: Pos;
|
|
211
|
+
} | {
|
|
212
|
+
kind: "StopTimer";
|
|
213
|
+
name: string;
|
|
214
|
+
pos: Pos;
|
|
193
215
|
} | {
|
|
194
216
|
kind: "ForStmt";
|
|
195
217
|
bind: string;
|
|
@@ -415,9 +437,30 @@ type TileProp = {
|
|
|
415
437
|
value: Expr;
|
|
416
438
|
};
|
|
417
439
|
//#endregion
|
|
440
|
+
//#region src/capabilities.d.ts
|
|
441
|
+
/** Capabilities that may appear in `app.caps` without any manifest. */
|
|
442
|
+
declare const STANDARD_CAPABILITIES: ReadonlySet<string>;
|
|
443
|
+
type CapabilityManifest = {
|
|
444
|
+
capabilities: string[];
|
|
445
|
+
};
|
|
446
|
+
type ManifestResult = {
|
|
447
|
+
ok: true;
|
|
448
|
+
manifest: CapabilityManifest;
|
|
449
|
+
} | {
|
|
450
|
+
ok: false;
|
|
451
|
+
error: string;
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Validate a parsed `kumiki.caps.json` value. Accepts either bare strings or
|
|
455
|
+
* `{ name, description? }` objects in the `capabilities` array. Pure — the
|
|
456
|
+
* caller does the file read + JSON parse and reports the location.
|
|
457
|
+
*/
|
|
458
|
+
declare function parseCapabilityManifest(raw: unknown): ManifestResult;
|
|
459
|
+
//#endregion
|
|
418
460
|
//#region src/codegen.d.ts
|
|
419
461
|
type CodegenOptions = {
|
|
420
|
-
runtimeSpecifier: string;
|
|
462
|
+
runtimeSpecifier: string; /** Emit the in-language `test` definitions (`__kumikiTests`). Off for production builds. */
|
|
463
|
+
includeTests?: boolean;
|
|
421
464
|
};
|
|
422
465
|
declare function codegen(program: Program, opts: CodegenOptions): string;
|
|
423
466
|
declare const RUNTIME_HELPERS = "\nfunction _setPath(obj, path, value) {\n if (path.length === 0) return value;\n const [head, ...rest] = path;\n const cur = obj ?? {};\n return { ...cur, [head]: _setPath(cur[head], rest, value) };\n}\nfunction _children(...xs) {\n const out = [];\n for (const x of xs) {\n if (x === null || x === undefined) continue;\n if (Array.isArray(x)) {\n for (const y of x) if (y !== null && y !== undefined) out.push(y);\n } else {\n out.push(x);\n }\n }\n return out;\n}\nfunction _attachProps(node, props) {\n if (!node || !props) return node;\n return { ...node, props: { ...(node.props || {}), ...props } };\n}\n";
|
|
@@ -429,9 +472,15 @@ type KumikiError = {
|
|
|
429
472
|
message: string;
|
|
430
473
|
pos: Pos;
|
|
431
474
|
};
|
|
432
|
-
/**
|
|
475
|
+
/**
|
|
476
|
+
* Returns errors with a11y warnings filtered out (unless strict).
|
|
477
|
+
* `capabilities` lists project-registered capabilities (from a
|
|
478
|
+
* `kumiki.caps.json` manifest) that are accepted in `app.caps` in addition to
|
|
479
|
+
* the standard set.
|
|
480
|
+
*/
|
|
433
481
|
declare function check(program: Program, opts?: {
|
|
434
482
|
strictA11y?: boolean;
|
|
483
|
+
capabilities?: string[];
|
|
435
484
|
}): KumikiError[];
|
|
436
485
|
//#endregion
|
|
437
486
|
//#region src/compile.d.ts
|
|
@@ -453,7 +502,8 @@ type ExtendedCodegenOptions = CodegenOptions & {
|
|
|
453
502
|
* Node-only imports, so it can run unchanged in the browser. Node callers can
|
|
454
503
|
* use `nodeRuntimeBundleReader` from `@kumikijs/compiler/node`.
|
|
455
504
|
*/
|
|
456
|
-
readRuntimeBundle?: () => string;
|
|
505
|
+
readRuntimeBundle?: () => string; /** Project-registered capabilities (from `kumiki.caps.json`) accepted in `app.caps`. */
|
|
506
|
+
capabilities?: string[];
|
|
457
507
|
};
|
|
458
508
|
/** Inline a runtime bundle into generated module code, stripping the bridging import/export lines. */
|
|
459
509
|
declare function inlineRuntime(generatedJs: string, runtimeBundleJs: string): string;
|
|
@@ -469,4 +519,4 @@ declare class ParseError extends Error {
|
|
|
469
519
|
}
|
|
470
520
|
declare function parse(tokens: Token[]): Program;
|
|
471
521
|
//#endregion
|
|
472
|
-
export { type AppDef, type BinOp, type CodegenOptions, type CompileFail, type CompileOk, type CompileResult, type Def, type EffectDef, type EventPattern, type Expr, type ExtendedCodegenOptions, type FnDef, type KumikiError, type Lvalue, type MatchArm, ParseError, type Pattern, type PolicyExpr, type Pos, type Program, RUNTIME_HELPERS, type ReducerDef, type Refinement, type RetryExpr, type SlotDef, type Statement, type ThemeDef, type ThemeValue, type TileArg, type TileDef, type TileExpr, type TileMatchArm, type TileProp, type Token, type TypeDef, type TypeExpr, type UiEventKind, check, codegen, compile, inlineRuntime, lex, parse };
|
|
522
|
+
export { type AppDef, type BinOp, type CapabilityManifest, type CodegenOptions, type CompileFail, type CompileOk, type CompileResult, type Def, type EffectDef, type EventPattern, type Expr, type ExtendedCodegenOptions, type FnDef, type KumikiError, type Lvalue, type ManifestResult, type MatchArm, type MotionDef, ParseError, type Pattern, type PolicyExpr, type Pos, type Program, RUNTIME_HELPERS, type ReducerDef, type Refinement, type RetryExpr, STANDARD_CAPABILITIES, type SlotDef, type Statement, type TestDef, type ThemeDef, type ThemeValue, type TileArg, type TileDef, type TileExpr, type TileMatchArm, type TileProp, type Token, type TypeDef, type TypeExpr, type UiEventKind, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as parseCapabilityManifest, t as STANDARD_CAPABILITIES } from "./capabilities-Bdub5oAe.js";
|
|
1
2
|
//#region src/codegen.ts
|
|
2
3
|
function codegen(program, opts) {
|
|
3
4
|
const types = new Map(program.defs.filter((d) => d.kind === "TypeDef").map((d) => [d.name, d]));
|
|
@@ -8,6 +9,8 @@ function codegen(program, opts) {
|
|
|
8
9
|
const tiles = program.defs.filter((d) => d.kind === "TileDef");
|
|
9
10
|
const apps = program.defs.filter((d) => d.kind === "AppDef");
|
|
10
11
|
const themes = program.defs.filter((d) => d.kind === "ThemeDef");
|
|
12
|
+
const motions = program.defs.filter((d) => d.kind === "MotionDef");
|
|
13
|
+
const tests = program.defs.filter((d) => d.kind === "TestDef");
|
|
11
14
|
const app = apps[0];
|
|
12
15
|
if (!app) throw new Error("No app definition found");
|
|
13
16
|
const ctx = {
|
|
@@ -59,6 +62,10 @@ function codegen(program, opts) {
|
|
|
59
62
|
lines.push("};");
|
|
60
63
|
const themeRef = app.theme ? JSON.stringify(app.theme) : "null";
|
|
61
64
|
lines.push("");
|
|
65
|
+
lines.push("const _motions = {");
|
|
66
|
+
for (const m of motions) lines.push(` ${JSON.stringify(m.name)}: ${JSON.stringify(m.body)},`);
|
|
67
|
+
lines.push("};");
|
|
68
|
+
lines.push("");
|
|
62
69
|
lines.push("const App = {");
|
|
63
70
|
lines.push(" slots: _slots,");
|
|
64
71
|
lines.push(` caps: ${JSON.stringify(app.caps)},`);
|
|
@@ -69,12 +76,110 @@ function codegen(program, opts) {
|
|
|
69
76
|
lines.push(" live: _live,");
|
|
70
77
|
lines.push(" themes: _themes,");
|
|
71
78
|
lines.push(` themeName: ${themeRef},`);
|
|
79
|
+
lines.push(" motions: _motions,");
|
|
72
80
|
lines.push("};");
|
|
73
81
|
lines.push("");
|
|
74
82
|
lines.push("globalThis.__kumikiApp = App;");
|
|
83
|
+
if (opts.includeTests && tests.length > 0) {
|
|
84
|
+
lines.push("");
|
|
85
|
+
lines.push("const _tilesById = {");
|
|
86
|
+
for (const tile of tiles) lines.push(` ${JSON.stringify(tile.name)}: (${jsName("$1")}) => ${genTile(tile, ctx)},`);
|
|
87
|
+
lines.push("};");
|
|
88
|
+
lines.push("void _tilesById;");
|
|
89
|
+
lines.push("const __kumikiTests = [");
|
|
90
|
+
for (const t of tests) lines.push(genTest(t, ctx));
|
|
91
|
+
lines.push("];");
|
|
92
|
+
lines.push("globalThis.__kumikiTests = __kumikiTests;");
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
75
95
|
lines.push(`mount(App, document.getElementById("root"));`);
|
|
76
96
|
return lines.join("\n");
|
|
77
97
|
}
|
|
98
|
+
function recordField(e, name) {
|
|
99
|
+
if (e.kind !== "RecordLit") return void 0;
|
|
100
|
+
return e.fields.find((f) => f.name === name)?.value;
|
|
101
|
+
}
|
|
102
|
+
function genTest(t, gen) {
|
|
103
|
+
const ctx = makeEvalCtx(gen, /* @__PURE__ */ new Set());
|
|
104
|
+
const nameJs = JSON.stringify(t.name);
|
|
105
|
+
if (t.testKind === "reducer-test") {
|
|
106
|
+
const slots = recordField(t.given, "slots");
|
|
107
|
+
const event = recordField(t.given, "event");
|
|
108
|
+
const slotsJs = slots ? jsOfExpr(slots, ctx) : "({})";
|
|
109
|
+
const elJs = eventPayloadJs(event, ctx);
|
|
110
|
+
const panic = recordField(t.expect, "panic");
|
|
111
|
+
let expectJs;
|
|
112
|
+
if (panic) expectJs = `{ kind: "panic", message: ${jsOfExpr(panic, ctx)} }`;
|
|
113
|
+
else {
|
|
114
|
+
const xs = recordField(t.expect, "slots");
|
|
115
|
+
const xe = recordField(t.expect, "effects");
|
|
116
|
+
expectJs = `{ kind: "state", slots: ${xs ? jsOfExpr(xs, ctx) : "({})"}, effects: ${xe ? effectListJs(xe, ctx) : "[]"} }`;
|
|
117
|
+
}
|
|
118
|
+
return ` {
|
|
119
|
+
name: ${nameJs},
|
|
120
|
+
kind: "reducer-test",
|
|
121
|
+
run: () => {
|
|
122
|
+
_s.resetLive(_live, _slots, ${slotsJs});
|
|
123
|
+
const _el = ${elJs};
|
|
124
|
+
const _r = _reducers.find((r) => r.name === ${JSON.stringify(t.target)});
|
|
125
|
+
if (!_r) throw new Error("reducer ${t.target} not found");
|
|
126
|
+
let _res = null, _panic = null;
|
|
127
|
+
try { _res = _r.apply(_live, { $el: _el, $event: _el }); }
|
|
128
|
+
catch (e) { _panic = (e && e.message) ? e.message : String(e); }
|
|
129
|
+
return _s.runReducerTest({ name: ${nameJs}, givenSlots: { ..._live }, result: _res, panic: _panic, expect: ${expectJs} });
|
|
130
|
+
},
|
|
131
|
+
},`;
|
|
132
|
+
}
|
|
133
|
+
const slots = recordField(t.given, "slots");
|
|
134
|
+
const slotsJs = slots ? jsOfExpr(slots, ctx) : "({})";
|
|
135
|
+
const inField = recordField(t.given, "in");
|
|
136
|
+
const inJs = inField ? jsOfExpr(inField, ctx) : "undefined";
|
|
137
|
+
const expectedJs = tileExprJs(t.expect, gen, ctx);
|
|
138
|
+
return ` {
|
|
139
|
+
name: ${nameJs},
|
|
140
|
+
kind: "tile-test",
|
|
141
|
+
run: () => {
|
|
142
|
+
_s.resetLive(_live, _slots, ${slotsJs});
|
|
143
|
+
const _actual = _tilesById[${JSON.stringify(t.target)}](${inJs});
|
|
144
|
+
const _expected = ${expectedJs};
|
|
145
|
+
return _s.runTileTest({ name: ${nameJs}, actual: _actual, expected: _expected });
|
|
146
|
+
},
|
|
147
|
+
},`;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Compile a `[eff(a), ...]` expect.effects list into `[{effect, args, argsSpecified}]`.
|
|
151
|
+
* A bare name (`persist`) matches by name only (`argsSpecified: false`); a call
|
|
152
|
+
* (`persist(x)`, even `persist()`) pins the exact arguments (`argsSpecified: true`).
|
|
153
|
+
*/
|
|
154
|
+
function effectListJs(e, ctx) {
|
|
155
|
+
if (e.kind !== "ListLit") return "[]";
|
|
156
|
+
return `[${e.items.map((it) => {
|
|
157
|
+
if (it.kind === "Call") {
|
|
158
|
+
const args = it.args.map((a) => jsOfExpr(a, ctx)).join(", ");
|
|
159
|
+
return `{ effect: ${JSON.stringify(it.callee)}, args: [${args}], argsSpecified: true }`;
|
|
160
|
+
}
|
|
161
|
+
if (it.kind === "Ref") return `{ effect: ${JSON.stringify(it.name)}, args: [], argsSpecified: false }`;
|
|
162
|
+
return `{ effect: "?", args: [], argsSpecified: false }`;
|
|
163
|
+
}).join(", ")}]`;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The reducer payload (`$el` / `$event`) for a reducer-test's `given.event`.
|
|
167
|
+
* Uses `el` when present (spec §8.5), otherwise the event's other fields
|
|
168
|
+
* (everything except `type` / `target`) so flat `{type, target, value}` forms
|
|
169
|
+
* still reach the reducer.
|
|
170
|
+
*/
|
|
171
|
+
function eventPayloadJs(event, ctx) {
|
|
172
|
+
if (event?.kind !== "RecordLit") return "({})";
|
|
173
|
+
const el = event.fields.find((f) => f.name === "el");
|
|
174
|
+
if (el) return jsOfExpr(el.value, ctx);
|
|
175
|
+
const rest = event.fields.filter((f) => f.name !== "type" && f.name !== "target");
|
|
176
|
+
if (rest.length === 0) return "({})";
|
|
177
|
+
return jsOfExpr({
|
|
178
|
+
kind: "RecordLit",
|
|
179
|
+
fields: rest,
|
|
180
|
+
pos: event.pos
|
|
181
|
+
}, ctx);
|
|
182
|
+
}
|
|
78
183
|
function makeEvalCtx(gen, locals, reducerScope = false) {
|
|
79
184
|
return {
|
|
80
185
|
gen,
|
|
@@ -145,11 +250,14 @@ function genReducer(r, gen) {
|
|
|
145
250
|
eventJs = `{ kind: "ui", ev: ${JSON.stringify(r.on.ev)} }`;
|
|
146
251
|
selectorJs = `{ tile: ${JSON.stringify(r.on.selector.tile)}${r.on.selector.id ? `, id: ${JSON.stringify(r.on.selector.id)}` : ""} }`;
|
|
147
252
|
} else if (r.on.kind === "EffectEvent") eventJs = `{ kind: "effect", effect: ${JSON.stringify(r.on.effect)}, outcome: ${JSON.stringify(r.on.outcome)} }`;
|
|
148
|
-
else if (r.on.kind === "TimerEvent")
|
|
149
|
-
|
|
253
|
+
else if (r.on.kind === "TimerEvent") {
|
|
254
|
+
const nameJs = r.on.name !== void 0 ? `, name: ${JSON.stringify(r.on.name)}` : "";
|
|
255
|
+
eventJs = `{ kind: "timer", intervalMs: ${r.on.intervalMs}${nameJs} }`;
|
|
256
|
+
} else eventJs = `{ kind: "lifecycle", name: ${JSON.stringify(r.on.name)} }`;
|
|
150
257
|
const stmtLines = [];
|
|
151
258
|
stmtLines.push(`const _next = {};`);
|
|
152
259
|
stmtLines.push(`const _emits = [];`);
|
|
260
|
+
stmtLines.push(`const _stops = [];`);
|
|
153
261
|
if (r.on.kind === "EffectEvent") for (let i = 0; i < r.on.binds.length; i++) {
|
|
154
262
|
const name = r.on.binds[i];
|
|
155
263
|
if (name === "_") continue;
|
|
@@ -159,7 +267,7 @@ function genReducer(r, gen) {
|
|
|
159
267
|
stmtLines.push(`const ${jsName("$event")} = _payload.$event || _payload || {};`);
|
|
160
268
|
stmtLines.push(`const ${jsName("$route")} = _payload.$route || {};`);
|
|
161
269
|
for (const st of r.do) stmtLines.push(genStatement(st, ctx));
|
|
162
|
-
stmtLines.push(`return { slots: _next, emits: _emits };`);
|
|
270
|
+
stmtLines.push(`return { slots: _next, emits: _emits, stopTimers: _stops };`);
|
|
163
271
|
return ` {
|
|
164
272
|
name: ${JSON.stringify(r.name)},
|
|
165
273
|
selector: ${selectorJs},
|
|
@@ -206,6 +314,7 @@ function genStatement(s, ctx) {
|
|
|
206
314
|
const args = s.args.map((a) => jsOfExpr(a, ctx)).join(", ");
|
|
207
315
|
return `_emits.push({ effect: ${JSON.stringify(s.effect)}, args: [${args}] });`;
|
|
208
316
|
}
|
|
317
|
+
if (s.kind === "StopTimer") return `_stops.push(${JSON.stringify(s.name)});`;
|
|
209
318
|
return genSlotAssign(s.lvalue, s.rhs, ctx);
|
|
210
319
|
}
|
|
211
320
|
function genSlotAssign(lv, rhs, ctx) {
|
|
@@ -529,6 +638,7 @@ const BUILTIN_TILES$2 = new Set([
|
|
|
529
638
|
"panel",
|
|
530
639
|
"grid",
|
|
531
640
|
"stack",
|
|
641
|
+
"overlay",
|
|
532
642
|
"region",
|
|
533
643
|
"scroll",
|
|
534
644
|
"divider",
|
|
@@ -621,6 +731,7 @@ function tileCallJs(t, gen, ctx, enclosingTile) {
|
|
|
621
731
|
case "box":
|
|
622
732
|
case "grid":
|
|
623
733
|
case "stack":
|
|
734
|
+
case "overlay":
|
|
624
735
|
case "region":
|
|
625
736
|
case "scroll":
|
|
626
737
|
case "divider":
|
|
@@ -873,6 +984,7 @@ const KEYWORDS = new Set([
|
|
|
873
984
|
"tile",
|
|
874
985
|
"fn",
|
|
875
986
|
"app",
|
|
987
|
+
"test",
|
|
876
988
|
"nominal",
|
|
877
989
|
"where",
|
|
878
990
|
"when",
|
|
@@ -1172,6 +1284,7 @@ const BUILTIN_TILES$1 = new Set([
|
|
|
1172
1284
|
"row",
|
|
1173
1285
|
"column",
|
|
1174
1286
|
"stack",
|
|
1287
|
+
"overlay",
|
|
1175
1288
|
"grid",
|
|
1176
1289
|
"box",
|
|
1177
1290
|
"card",
|
|
@@ -1272,6 +1385,10 @@ var Parser = class {
|
|
|
1272
1385
|
defs.push(this.parseThemeDef());
|
|
1273
1386
|
continue;
|
|
1274
1387
|
}
|
|
1388
|
+
if (this.matchT("ident", "motion")) {
|
|
1389
|
+
defs.push(this.parseMotionDef());
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1275
1392
|
defs.push(this.parseDef());
|
|
1276
1393
|
}
|
|
1277
1394
|
return {
|
|
@@ -1290,6 +1407,17 @@ var Parser = class {
|
|
|
1290
1407
|
pos: start.pos
|
|
1291
1408
|
};
|
|
1292
1409
|
}
|
|
1410
|
+
parseMotionDef() {
|
|
1411
|
+
const start = this.eat("ident", "motion");
|
|
1412
|
+
const name = this.eat("ident").value;
|
|
1413
|
+
this.eat("op", "=");
|
|
1414
|
+
return {
|
|
1415
|
+
kind: "MotionDef",
|
|
1416
|
+
name,
|
|
1417
|
+
body: this.parseThemeRecord(),
|
|
1418
|
+
pos: start.pos
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1293
1421
|
parseThemeRecord() {
|
|
1294
1422
|
this.eat("op", "{");
|
|
1295
1423
|
const out = {};
|
|
@@ -1331,6 +1459,7 @@ var Parser = class {
|
|
|
1331
1459
|
case "fn": return this.parseFn();
|
|
1332
1460
|
case "effect": return this.parseEffect();
|
|
1333
1461
|
case "app": return this.parseApp();
|
|
1462
|
+
case "test": return this.parseTest();
|
|
1334
1463
|
default: throw new ParseError(`Unsupported definition keyword "${t.value}"`, t.pos);
|
|
1335
1464
|
}
|
|
1336
1465
|
}
|
|
@@ -1582,10 +1711,23 @@ var Parser = class {
|
|
|
1582
1711
|
this.next();
|
|
1583
1712
|
this.eat("op", "(");
|
|
1584
1713
|
const intervalMs = this.parseDuration();
|
|
1714
|
+
let name;
|
|
1715
|
+
if (this.matchOp(",")) {
|
|
1716
|
+
this.next();
|
|
1717
|
+
const kw = this.eat("ident");
|
|
1718
|
+
if (kw.value !== "name") throw new ParseError(`Expected "name=" in timer(...)`, kw.pos);
|
|
1719
|
+
this.eat("op", "=");
|
|
1720
|
+
name = this.eat("ident").value;
|
|
1721
|
+
}
|
|
1585
1722
|
this.eat("op", ")");
|
|
1586
|
-
return {
|
|
1723
|
+
return name === void 0 ? {
|
|
1724
|
+
kind: "TimerEvent",
|
|
1725
|
+
intervalMs,
|
|
1726
|
+
pos: t.pos
|
|
1727
|
+
} : {
|
|
1587
1728
|
kind: "TimerEvent",
|
|
1588
1729
|
intervalMs,
|
|
1730
|
+
name,
|
|
1589
1731
|
pos: t.pos
|
|
1590
1732
|
};
|
|
1591
1733
|
}
|
|
@@ -1772,6 +1914,18 @@ var Parser = class {
|
|
|
1772
1914
|
pos: start.pos
|
|
1773
1915
|
};
|
|
1774
1916
|
}
|
|
1917
|
+
const cur = this.peek();
|
|
1918
|
+
if (cur.kind === "ident" && cur.value === "stop-timer") {
|
|
1919
|
+
this.next();
|
|
1920
|
+
this.eat("op", "(");
|
|
1921
|
+
const name = this.eat("ident").value;
|
|
1922
|
+
this.eat("op", ")");
|
|
1923
|
+
return {
|
|
1924
|
+
kind: "StopTimer",
|
|
1925
|
+
name,
|
|
1926
|
+
pos: cur.pos
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1775
1929
|
const lvalue = this.parseLvalue();
|
|
1776
1930
|
this.eat("op", ":=");
|
|
1777
1931
|
return {
|
|
@@ -2279,25 +2433,30 @@ var Parser = class {
|
|
|
2279
2433
|
};
|
|
2280
2434
|
}
|
|
2281
2435
|
let isRecord = false;
|
|
2282
|
-
|
|
2436
|
+
const k0 = this.peek();
|
|
2437
|
+
if (k0.kind === "ident" || k0.kind === "kw") {
|
|
2283
2438
|
const peek1 = this.peek(1);
|
|
2284
2439
|
if (peek1.kind === "op" && (peek1.value === "=" || peek1.value === ":" || peek1.value === "," || peek1.value === "}")) isRecord = true;
|
|
2285
2440
|
}
|
|
2286
2441
|
if (isRecord) {
|
|
2287
2442
|
const fields = [];
|
|
2288
2443
|
while (true) {
|
|
2289
|
-
const
|
|
2444
|
+
const keyTok = this.peek();
|
|
2445
|
+
if (keyTok.kind !== "ident" && keyTok.kind !== "kw") throw new ParseError("Expected a record field name", keyTok.pos);
|
|
2446
|
+
const fieldName = keyTok.value;
|
|
2447
|
+
const fieldPos = keyTok.pos;
|
|
2448
|
+
this.next();
|
|
2290
2449
|
let value;
|
|
2291
2450
|
if (this.matchOp("=") || this.matchOp(":")) {
|
|
2292
2451
|
this.next();
|
|
2293
2452
|
value = this.parseExpr();
|
|
2294
2453
|
} else value = {
|
|
2295
2454
|
kind: "Ref",
|
|
2296
|
-
name:
|
|
2297
|
-
pos:
|
|
2455
|
+
name: fieldName,
|
|
2456
|
+
pos: fieldPos
|
|
2298
2457
|
};
|
|
2299
2458
|
fields.push({
|
|
2300
|
-
name:
|
|
2459
|
+
name: fieldName,
|
|
2301
2460
|
value
|
|
2302
2461
|
});
|
|
2303
2462
|
if (!this.matchOp(",")) break;
|
|
@@ -2764,6 +2923,31 @@ var Parser = class {
|
|
|
2764
2923
|
if (t.kind === "kw") return true;
|
|
2765
2924
|
return false;
|
|
2766
2925
|
}
|
|
2926
|
+
parseTest() {
|
|
2927
|
+
const start = this.eat("kw", "test");
|
|
2928
|
+
const name = this.eat("ident").value;
|
|
2929
|
+
this.eat("op", "=");
|
|
2930
|
+
const kindTok = this.eat("ident");
|
|
2931
|
+
if (kindTok.value !== "reducer-test" && kindTok.value !== "tile-test") throw new ParseError(`Unknown test kind "${kindTok.value}" (expected reducer-test or tile-test)`, kindTok.pos);
|
|
2932
|
+
const target = this.eat("ident").value;
|
|
2933
|
+
const givenKw = this.eat("ident");
|
|
2934
|
+
if (givenKw.value !== "given") throw new ParseError(`Expected "given" in test "${name}"`, givenKw.pos);
|
|
2935
|
+
this.eat("op", "=");
|
|
2936
|
+
const given = this.parseExpr();
|
|
2937
|
+
const expectKw = this.eat("ident");
|
|
2938
|
+
if (expectKw.value !== "expect") throw new ParseError(`Expected "expect" in test "${name}"`, expectKw.pos);
|
|
2939
|
+
this.eat("op", "=");
|
|
2940
|
+
const expect = kindTok.value === "tile-test" ? this.parseTileExpr() : this.parseExpr();
|
|
2941
|
+
return {
|
|
2942
|
+
kind: "TestDef",
|
|
2943
|
+
name,
|
|
2944
|
+
testKind: kindTok.value,
|
|
2945
|
+
target,
|
|
2946
|
+
given,
|
|
2947
|
+
expect,
|
|
2948
|
+
pos: start.pos
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2767
2951
|
parseQualifiedList() {
|
|
2768
2952
|
this.eat("op", "[");
|
|
2769
2953
|
const out = [];
|
|
@@ -2838,6 +3022,7 @@ const BUILTIN_TILES = new Set([
|
|
|
2838
3022
|
"row",
|
|
2839
3023
|
"column",
|
|
2840
3024
|
"stack",
|
|
3025
|
+
"overlay",
|
|
2841
3026
|
"grid",
|
|
2842
3027
|
"box",
|
|
2843
3028
|
"card",
|
|
@@ -2886,13 +3071,18 @@ const A11Y_CODES = new Set([
|
|
|
2886
3071
|
"E0702",
|
|
2887
3072
|
"E0703"
|
|
2888
3073
|
]);
|
|
2889
|
-
/**
|
|
3074
|
+
/**
|
|
3075
|
+
* Returns errors with a11y warnings filtered out (unless strict).
|
|
3076
|
+
* `capabilities` lists project-registered capabilities (from a
|
|
3077
|
+
* `kumiki.caps.json` manifest) that are accepted in `app.caps` in addition to
|
|
3078
|
+
* the standard set.
|
|
3079
|
+
*/
|
|
2890
3080
|
function check(program, opts) {
|
|
2891
|
-
const errors = checkAll(program);
|
|
3081
|
+
const errors = checkAll(program, new Set(opts?.capabilities ?? []));
|
|
2892
3082
|
if (opts?.strictA11y) return errors;
|
|
2893
3083
|
return errors.filter((e) => !A11Y_CODES.has(e.code));
|
|
2894
3084
|
}
|
|
2895
|
-
function checkAll(program) {
|
|
3085
|
+
function checkAll(program, registeredCaps) {
|
|
2896
3086
|
const errors = [];
|
|
2897
3087
|
const sym = {
|
|
2898
3088
|
types: /* @__PURE__ */ new Map(),
|
|
@@ -2900,7 +3090,9 @@ function checkAll(program) {
|
|
|
2900
3090
|
reducers: /* @__PURE__ */ new Map(),
|
|
2901
3091
|
tiles: /* @__PURE__ */ new Map(),
|
|
2902
3092
|
fns: /* @__PURE__ */ new Map(),
|
|
2903
|
-
effects: /* @__PURE__ */ new Map()
|
|
3093
|
+
effects: /* @__PURE__ */ new Map(),
|
|
3094
|
+
timerNames: /* @__PURE__ */ new Set(),
|
|
3095
|
+
motions: /* @__PURE__ */ new Set()
|
|
2904
3096
|
};
|
|
2905
3097
|
for (const def of program.defs) switch (def.kind) {
|
|
2906
3098
|
case "TypeDef":
|
|
@@ -2911,6 +3103,13 @@ function checkAll(program) {
|
|
|
2911
3103
|
break;
|
|
2912
3104
|
case "ReducerDef":
|
|
2913
3105
|
sym.reducers.set(def.name, def);
|
|
3106
|
+
if (def.on.kind === "TimerEvent" && def.on.name !== void 0) if (sym.timerNames.has(def.on.name)) errors.push({
|
|
3107
|
+
code: "E0002",
|
|
3108
|
+
kind: "duplicate-timer-name",
|
|
3109
|
+
message: `Timer name "${def.on.name}" is declared more than once`,
|
|
3110
|
+
pos: def.on.pos
|
|
3111
|
+
});
|
|
3112
|
+
else sym.timerNames.add(def.on.name);
|
|
2914
3113
|
break;
|
|
2915
3114
|
case "TileDef":
|
|
2916
3115
|
sym.tiles.set(def.name, def);
|
|
@@ -2921,6 +3120,9 @@ function checkAll(program) {
|
|
|
2921
3120
|
case "EffectDef":
|
|
2922
3121
|
sym.effects.set(def.name, def);
|
|
2923
3122
|
break;
|
|
3123
|
+
case "MotionDef":
|
|
3124
|
+
sym.motions.add(def.name);
|
|
3125
|
+
break;
|
|
2924
3126
|
case "AppDef":
|
|
2925
3127
|
sym.app = def;
|
|
2926
3128
|
break;
|
|
@@ -2931,10 +3133,137 @@ function checkAll(program) {
|
|
|
2931
3133
|
if (def.kind === "ReducerDef") checkReducer(def, sym, errors);
|
|
2932
3134
|
if (def.kind === "FnDef") checkFn(def, sym, errors);
|
|
2933
3135
|
if (def.kind === "EffectDef") checkEffect(def, sym, errors);
|
|
2934
|
-
if (def.kind === "AppDef") checkApp(def, sym, errors);
|
|
3136
|
+
if (def.kind === "AppDef") checkApp(def, sym, errors, registeredCaps);
|
|
3137
|
+
if (def.kind === "MotionDef") checkMotion(def, errors);
|
|
3138
|
+
if (def.kind === "TestDef") checkTest(def, sym, errors);
|
|
2935
3139
|
}
|
|
2936
3140
|
return errors;
|
|
2937
3141
|
}
|
|
3142
|
+
const MOTION_KEYFRAME_PROPS = new Set([
|
|
3143
|
+
"opacity",
|
|
3144
|
+
"translate-x",
|
|
3145
|
+
"translate-y",
|
|
3146
|
+
"scale",
|
|
3147
|
+
"rotate"
|
|
3148
|
+
]);
|
|
3149
|
+
const MOTION_EASINGS = new Set([
|
|
3150
|
+
"linear",
|
|
3151
|
+
"ease",
|
|
3152
|
+
"ease-in",
|
|
3153
|
+
"ease-out",
|
|
3154
|
+
"ease-in-out"
|
|
3155
|
+
]);
|
|
3156
|
+
const MOTION_DURATION_TOKENS = new Set([
|
|
3157
|
+
"fast",
|
|
3158
|
+
"normal",
|
|
3159
|
+
"slow"
|
|
3160
|
+
]);
|
|
3161
|
+
const MOTION_DIRECTIONS = new Set([
|
|
3162
|
+
"normal",
|
|
3163
|
+
"reverse",
|
|
3164
|
+
"alternate",
|
|
3165
|
+
"alternate-reverse"
|
|
3166
|
+
]);
|
|
3167
|
+
const MOTION_TIMING_KEYS = new Set([
|
|
3168
|
+
"duration",
|
|
3169
|
+
"easing",
|
|
3170
|
+
"iteration",
|
|
3171
|
+
"direction"
|
|
3172
|
+
]);
|
|
3173
|
+
/** `duration` (ms) and `iteration` are spec'd as positive integers (no 0 / negative / float). */
|
|
3174
|
+
const isPositiveInt = (v) => typeof v === "number" && Number.isInteger(v) && v > 0;
|
|
3175
|
+
/**
|
|
3176
|
+
* Validate a `motion` definition against the closed grammar (ADR-001). Purity
|
|
3177
|
+
* (no slots/effects) is already guaranteed by the parser — the body is a literal
|
|
3178
|
+
* record — so this only enforces the closed property + timing vocabularies.
|
|
3179
|
+
*/
|
|
3180
|
+
function checkMotion(def, errors) {
|
|
3181
|
+
const body = def.body;
|
|
3182
|
+
const keyframes = body.keyframes;
|
|
3183
|
+
if (typeof keyframes !== "object" || Array.isArray(keyframes)) {
|
|
3184
|
+
errors.push({
|
|
3185
|
+
code: "E0403",
|
|
3186
|
+
kind: "motion-malformed",
|
|
3187
|
+
message: `motion "${def.name}" must declare a "keyframes" record`,
|
|
3188
|
+
pos: def.pos
|
|
3189
|
+
});
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
const stops = keyframes;
|
|
3193
|
+
for (const required of ["from", "to"]) {
|
|
3194
|
+
const stop = stops[required];
|
|
3195
|
+
if (typeof stop !== "object" || Array.isArray(stop)) {
|
|
3196
|
+
errors.push({
|
|
3197
|
+
code: "E0403",
|
|
3198
|
+
kind: "motion-malformed",
|
|
3199
|
+
message: `motion "${def.name}" keyframes must include a "${required}" record`,
|
|
3200
|
+
pos: def.pos
|
|
3201
|
+
});
|
|
3202
|
+
return;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
for (const stopName of Object.keys(stops)) {
|
|
3206
|
+
if (stopName !== "from" && stopName !== "to") {
|
|
3207
|
+
errors.push({
|
|
3208
|
+
code: "E0403",
|
|
3209
|
+
kind: "motion-malformed",
|
|
3210
|
+
message: `motion "${def.name}" keyframes support only "from" / "to" (got "${stopName}")`,
|
|
3211
|
+
pos: def.pos
|
|
3212
|
+
});
|
|
3213
|
+
continue;
|
|
3214
|
+
}
|
|
3215
|
+
const stop = stops[stopName];
|
|
3216
|
+
for (const [prop, val] of Object.entries(stop)) if (!MOTION_KEYFRAME_PROPS.has(prop)) errors.push({
|
|
3217
|
+
code: "E0401",
|
|
3218
|
+
kind: "motion-unknown-property",
|
|
3219
|
+
message: `motion "${def.name}": unknown keyframe property "${prop}" (allowed: ${[...MOTION_KEYFRAME_PROPS].join(", ")})`,
|
|
3220
|
+
pos: def.pos
|
|
3221
|
+
});
|
|
3222
|
+
else if (typeof val !== "number") errors.push({
|
|
3223
|
+
code: "E0401",
|
|
3224
|
+
kind: "motion-unknown-property",
|
|
3225
|
+
message: `motion "${def.name}": keyframe property "${prop}" must be a number`,
|
|
3226
|
+
pos: def.pos
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
for (const key of Object.keys(body)) {
|
|
3230
|
+
if (key === "keyframes") continue;
|
|
3231
|
+
if (!MOTION_TIMING_KEYS.has(key)) errors.push({
|
|
3232
|
+
code: "E0402",
|
|
3233
|
+
kind: "motion-invalid-timing",
|
|
3234
|
+
message: `motion "${def.name}": unknown field "${key}" (allowed: keyframes, ${[...MOTION_TIMING_KEYS].join(", ")})`,
|
|
3235
|
+
pos: def.pos
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
const dur = body.duration;
|
|
3239
|
+
if (dur !== void 0 && !(isPositiveInt(dur) || MOTION_DURATION_TOKENS.has(String(dur)))) errors.push({
|
|
3240
|
+
code: "E0402",
|
|
3241
|
+
kind: "motion-invalid-timing",
|
|
3242
|
+
message: `motion "${def.name}": duration must be a positive Int (ms) or one of fast/normal/slow`,
|
|
3243
|
+
pos: def.pos
|
|
3244
|
+
});
|
|
3245
|
+
const eas = body.easing;
|
|
3246
|
+
if (eas !== void 0 && !MOTION_EASINGS.has(String(eas))) errors.push({
|
|
3247
|
+
code: "E0402",
|
|
3248
|
+
kind: "motion-invalid-timing",
|
|
3249
|
+
message: `motion "${def.name}": easing must be one of ${[...MOTION_EASINGS].join(", ")}`,
|
|
3250
|
+
pos: def.pos
|
|
3251
|
+
});
|
|
3252
|
+
const iter = body.iteration;
|
|
3253
|
+
if (iter !== void 0 && !(isPositiveInt(iter) || iter === "infinite")) errors.push({
|
|
3254
|
+
code: "E0402",
|
|
3255
|
+
kind: "motion-invalid-timing",
|
|
3256
|
+
message: `motion "${def.name}": iteration must be a positive Int or "infinite"`,
|
|
3257
|
+
pos: def.pos
|
|
3258
|
+
});
|
|
3259
|
+
const dir = body.direction;
|
|
3260
|
+
if (dir !== void 0 && !MOTION_DIRECTIONS.has(String(dir))) errors.push({
|
|
3261
|
+
code: "E0402",
|
|
3262
|
+
kind: "motion-invalid-timing",
|
|
3263
|
+
message: `motion "${def.name}": direction must be one of ${[...MOTION_DIRECTIONS].join(", ")}`,
|
|
3264
|
+
pos: def.pos
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
2938
3267
|
function checkSlot(slot, sym, errors) {
|
|
2939
3268
|
resolveType(slot.type, sym, errors);
|
|
2940
3269
|
checkExpr(slot.init, sym, errors, {
|
|
@@ -3072,6 +3401,13 @@ function checkTileCall(t, sym, errors, ctx) {
|
|
|
3072
3401
|
message: `Reference to undefined reducer "${ref.name}"`,
|
|
3073
3402
|
pos: ref.pos
|
|
3074
3403
|
});
|
|
3404
|
+
} else if (prop.name === "motion" && prop.value.kind === "Str") {
|
|
3405
|
+
if (!sym.motions.has(prop.value.value)) errors.push({
|
|
3406
|
+
code: "E0107",
|
|
3407
|
+
kind: "undef-motion",
|
|
3408
|
+
message: `Reference to undefined motion "${prop.value.value}"`,
|
|
3409
|
+
pos: prop.value.pos
|
|
3410
|
+
});
|
|
3075
3411
|
} else checkExpr(prop.value, sym, errors, ctx);
|
|
3076
3412
|
}
|
|
3077
3413
|
function checkReducer(r, sym, errors) {
|
|
@@ -3158,6 +3494,15 @@ function checkStmt(s, sym, errors, ctx, writtenRoots) {
|
|
|
3158
3494
|
for (const a of s.args) checkExpr(a, sym, errors, ctx);
|
|
3159
3495
|
return;
|
|
3160
3496
|
}
|
|
3497
|
+
if (s.kind === "StopTimer") {
|
|
3498
|
+
if (!sym.timerNames.has(s.name)) errors.push({
|
|
3499
|
+
code: "E0106",
|
|
3500
|
+
kind: "undef-timer",
|
|
3501
|
+
message: `stop-timer refers to undefined timer name "${s.name}"`,
|
|
3502
|
+
pos: s.pos
|
|
3503
|
+
});
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3161
3506
|
const root = lvalueRoot(s.lvalue);
|
|
3162
3507
|
if (!sym.slots.has(root)) errors.push({
|
|
3163
3508
|
code: "E0103",
|
|
@@ -3325,7 +3670,34 @@ function checkEffect(eff, sym, errors) {
|
|
|
3325
3670
|
localBinds: new Set(["$1"])
|
|
3326
3671
|
});
|
|
3327
3672
|
}
|
|
3328
|
-
function
|
|
3673
|
+
function checkTest(t, sym, errors) {
|
|
3674
|
+
if (t.testKind === "reducer-test") {
|
|
3675
|
+
if (!sym.reducers.has(t.target)) errors.push({
|
|
3676
|
+
code: "E0102",
|
|
3677
|
+
kind: "undef-reducer",
|
|
3678
|
+
message: `Reference to undefined reducer "${t.target}"`,
|
|
3679
|
+
pos: t.pos
|
|
3680
|
+
});
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
if (!BUILTIN_TILES.has(t.target) && !sym.tiles.has(t.target)) errors.push({
|
|
3684
|
+
code: "E0105",
|
|
3685
|
+
kind: "undef-tile",
|
|
3686
|
+
message: `Reference to undefined tile "${t.target}"`,
|
|
3687
|
+
pos: t.pos
|
|
3688
|
+
});
|
|
3689
|
+
checkTileExpr(t.expect, sym, errors, {
|
|
3690
|
+
kind: "tile",
|
|
3691
|
+
localBinds: /* @__PURE__ */ new Set()
|
|
3692
|
+
});
|
|
3693
|
+
}
|
|
3694
|
+
function checkApp(app, sym, errors, registeredCaps) {
|
|
3695
|
+
for (const cap of app.caps) if (!STANDARD_CAPABILITIES.has(cap) && !registeredCaps.has(cap)) errors.push({
|
|
3696
|
+
code: "E0302",
|
|
3697
|
+
kind: "unknown-capability",
|
|
3698
|
+
message: `Unknown capability "${cap}" in app.caps — use a standard capability or register it in kumiki.caps.json`,
|
|
3699
|
+
pos: app.pos
|
|
3700
|
+
});
|
|
3329
3701
|
let saw404 = false;
|
|
3330
3702
|
for (const r of app.routes) {
|
|
3331
3703
|
if (r.tile.startsWith(">>")) continue;
|
|
@@ -3380,7 +3752,7 @@ function inlineRuntime(generatedJs, runtimeBundleJs) {
|
|
|
3380
3752
|
}
|
|
3381
3753
|
function compile(source, opts) {
|
|
3382
3754
|
const program = parse(lex(source));
|
|
3383
|
-
const errors = check(program);
|
|
3755
|
+
const errors = check(program, { capabilities: opts.capabilities ?? [] });
|
|
3384
3756
|
if (errors.length > 0) return {
|
|
3385
3757
|
kind: "fail",
|
|
3386
3758
|
errors
|
|
@@ -3397,4 +3769,4 @@ function compile(source, opts) {
|
|
|
3397
3769
|
};
|
|
3398
3770
|
}
|
|
3399
3771
|
//#endregion
|
|
3400
|
-
export { ParseError, RUNTIME_HELPERS, check, codegen, compile, inlineRuntime, lex, parse };
|
|
3772
|
+
export { ParseError, RUNTIME_HELPERS, STANDARD_CAPABILITIES, check, codegen, compile, inlineRuntime, lex, parse, parseCapabilityManifest };
|
package/dist/node.d.ts
CHANGED
|
@@ -4,5 +4,15 @@
|
|
|
4
4
|
* `compile(source, { bundle: true, readRuntimeBundle: nodeRuntimeBundleReader })`.
|
|
5
5
|
*/
|
|
6
6
|
declare function nodeRuntimeBundleReader(): string;
|
|
7
|
+
/** Thrown when a `kumiki.caps.json` exists but is malformed. */
|
|
8
|
+
declare class CapabilityManifestError extends Error {}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve project-registered capabilities from a `kumiki.caps.json` in the same
|
|
11
|
+
* directory as the given `.kumiki` file. Returns `[]` when no manifest exists;
|
|
12
|
+
* throws `CapabilityManifestError` (with the path) when one exists but is
|
|
13
|
+
* invalid. Pass the result as `compile(src, { capabilities })` /
|
|
14
|
+
* `check(program, { capabilities })`.
|
|
15
|
+
*/
|
|
16
|
+
declare function resolveCapabilities(kumikiFilePath: string): string[];
|
|
7
17
|
//#endregion
|
|
8
|
-
export { nodeRuntimeBundleReader };
|
|
18
|
+
export { CapabilityManifestError, nodeRuntimeBundleReader, resolveCapabilities };
|
package/dist/node.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { n as parseCapabilityManifest } from "./capabilities-Bdub5oAe.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
3
5
|
//#region src/node.ts
|
|
4
6
|
/**
|
|
5
7
|
* Reads the prebuilt @kumikijs/runtime bundle from disk. Pass as
|
|
@@ -8,5 +10,27 @@ import { readFileSync } from "node:fs";
|
|
|
8
10
|
function nodeRuntimeBundleReader() {
|
|
9
11
|
return readFileSync(createRequire(import.meta.url).resolve("@kumikijs/runtime/bundle"), "utf8");
|
|
10
12
|
}
|
|
13
|
+
/** Thrown when a `kumiki.caps.json` exists but is malformed. */
|
|
14
|
+
var CapabilityManifestError = class extends Error {};
|
|
15
|
+
/**
|
|
16
|
+
* Resolve project-registered capabilities from a `kumiki.caps.json` in the same
|
|
17
|
+
* directory as the given `.kumiki` file. Returns `[]` when no manifest exists;
|
|
18
|
+
* throws `CapabilityManifestError` (with the path) when one exists but is
|
|
19
|
+
* invalid. Pass the result as `compile(src, { capabilities })` /
|
|
20
|
+
* `check(program, { capabilities })`.
|
|
21
|
+
*/
|
|
22
|
+
function resolveCapabilities(kumikiFilePath) {
|
|
23
|
+
const manifestPath = join(dirname(kumikiFilePath), "kumiki.caps.json");
|
|
24
|
+
if (!existsSync(manifestPath)) return [];
|
|
25
|
+
let raw;
|
|
26
|
+
try {
|
|
27
|
+
raw = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
28
|
+
} catch (e) {
|
|
29
|
+
throw new CapabilityManifestError(`${manifestPath}: invalid JSON — ${e.message}`);
|
|
30
|
+
}
|
|
31
|
+
const result = parseCapabilityManifest(raw);
|
|
32
|
+
if (!result.ok) throw new CapabilityManifestError(`${manifestPath}: ${result.error}`);
|
|
33
|
+
return result.manifest.capabilities;
|
|
34
|
+
}
|
|
11
35
|
//#endregion
|
|
12
|
-
export { nodeRuntimeBundleReader };
|
|
36
|
+
export { CapabilityManifestError, nodeRuntimeBundleReader, resolveCapabilities };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kumikijs/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kumiki compiler — lexer, parser, typechecker, and code generator.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
|
-
"
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
30
|
},
|
|
31
31
|
"./node": {
|
|
32
32
|
"types": "./dist/node.d.ts",
|
|
33
|
-
"
|
|
33
|
+
"import": "./dist/node.js"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
@@ -38,30 +38,20 @@
|
|
|
38
38
|
],
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public",
|
|
41
|
-
"provenance": true
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
41
|
+
"provenance": true
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@kumikijs/runtime": "0.2.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^25.9.1",
|
|
48
|
+
"typescript": "^6.0.3",
|
|
49
|
+
"vitest": "^4.1.7"
|
|
52
50
|
},
|
|
53
51
|
"scripts": {
|
|
54
52
|
"build": "tsdown",
|
|
55
53
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
56
54
|
"test": "vitest run",
|
|
57
55
|
"lint": "biome check src test"
|
|
58
|
-
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"@kumikijs/runtime": "workspace:*"
|
|
61
|
-
},
|
|
62
|
-
"devDependencies": {
|
|
63
|
-
"@types/node": "catalog:",
|
|
64
|
-
"typescript": "catalog:",
|
|
65
|
-
"vitest": "catalog:"
|
|
66
56
|
}
|
|
67
|
-
}
|
|
57
|
+
}
|