buckyos 0.7.160 → 0.7.162
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/cli/dist/cli.mjs +412 -96
- package/cli/dist/cli.mjs.map +1 -1
- package/cli/modules/aiws.ts +385 -81
- package/cli/modules/pikg.ts +15 -6
- package/cli/modules/pikg_protocol.ts +9 -3
- package/cli/version.ts +1 -1
- package/dist/aiworkspace_client.d.ts +165 -1
- package/dist/aiworkspace_client.d.ts.map +1 -1
- package/dist/aiws_ext.cjs +1 -1
- package/dist/aiws_ext.cjs.map +1 -1
- package/dist/aiws_ext.d.ts +160 -19
- package/dist/aiws_ext.d.ts.map +1 -1
- package/dist/aiws_ext.mjs +817 -68
- package/dist/aiws_ext.mjs.map +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/{executor_client-50023bcc.mjs → executor_client-0856c3fc.mjs} +22 -2
- package/dist/executor_client-0856c3fc.mjs.map +1 -0
- package/dist/{executor_client-47edd42d.js → executor_client-3f7679e3.js} +9 -9
- package/dist/executor_client-3f7679e3.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +15 -3
- package/dist/node.cjs +1 -1
- package/dist/node.mjs +15 -3
- package/package.json +1 -1
- package/dist/executor_client-47edd42d.js.map +0 -1
- package/dist/executor_client-50023bcc.mjs.map +0 -1
package/dist/aiws_ext.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const AIWS_COMPONENT_FILE = "component.json";
|
|
2
|
+
const AIWS_COMPOSITION_FILE = "composition.json";
|
|
2
3
|
const AIWS_MANIFEST_FILE = "aiws-manifest.json";
|
|
3
4
|
const AIWS_BUILD_STATE_FILE = ".aiws-build.json";
|
|
4
|
-
const AIWS_MANIFEST_VERSION =
|
|
5
|
-
const AIWS_HTML_API_VERSION =
|
|
5
|
+
const AIWS_MANIFEST_VERSION = 3;
|
|
6
|
+
const AIWS_HTML_API_VERSION = 3;
|
|
6
7
|
const AIWS_DECLARATIVE_API_VERSION = 1;
|
|
8
|
+
const AIWS_TEMPLATE_FORMAT = "aiws-template/1";
|
|
7
9
|
const AIWS_MAX_SOURCE_FILES = 64;
|
|
8
10
|
class AiwsSourceError extends Error {
|
|
9
11
|
constructor(diagnostics) {
|
|
@@ -51,30 +53,132 @@ function base64(bytes) {
|
|
|
51
53
|
binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
52
54
|
return btoa(binary);
|
|
53
55
|
}
|
|
54
|
-
async function
|
|
56
|
+
async function packBuildFiles(files, upload, byRef = []) {
|
|
57
|
+
let total = files.reduce((n, f) => n + f.bytes.length, 0);
|
|
58
|
+
const uploaded = /* @__PURE__ */ new Map();
|
|
59
|
+
if (total > AIWS_MAX_INLINE_BYTES) {
|
|
60
|
+
if (!upload)
|
|
61
|
+
throw new AiwsSourceError([{ file: "", severity: "error", message: `a control's inline files are limited to ${AIWS_MAX_INLINE_BYTES} bytes per build` }]);
|
|
62
|
+
for (const f of [...files].sort((a, b) => b.bytes.length - a.bytes.length)) {
|
|
63
|
+
if (total <= AIWS_MAX_INLINE_BYTES)
|
|
64
|
+
break;
|
|
65
|
+
uploaded.set(f.name, await upload(f.name, f.bytes));
|
|
66
|
+
total -= f.bytes.length;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const out = [...byRef];
|
|
70
|
+
for (const f of files) {
|
|
71
|
+
const obj = uploaded.get(f.name);
|
|
72
|
+
out.push(obj ? { name: f.name, obj_id: obj } : { name: f.name, base64: base64(f.bytes) });
|
|
73
|
+
}
|
|
74
|
+
return out.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
75
|
+
}
|
|
76
|
+
async function buildFiles(nfsp, files, upload) {
|
|
55
77
|
var _a;
|
|
56
|
-
const
|
|
57
|
-
|
|
78
|
+
const byRef = [];
|
|
79
|
+
const inline = [];
|
|
58
80
|
for (const f of files) {
|
|
59
81
|
if ((_a = f.obj_id) == null ? void 0 : _a.startsWith("cyfile:")) {
|
|
60
|
-
|
|
82
|
+
byRef.push({ name: f.name, obj_id: f.obj_id });
|
|
61
83
|
continue;
|
|
62
84
|
}
|
|
63
85
|
if (!f.node_id)
|
|
64
86
|
throw new AiwsSourceError([{ file: f.name, severity: "error", message: "the file service did not name this file" }]);
|
|
65
|
-
|
|
66
|
-
inline += bytes.length;
|
|
67
|
-
if (inline > AIWS_MAX_INLINE_BYTES)
|
|
68
|
-
throw new AiwsSourceError([{ file: f.name, severity: "error", message: `a control's files are limited to ${AIWS_MAX_INLINE_BYTES} bytes per build` }]);
|
|
69
|
-
out.push({ name: f.name, base64: base64(bytes) });
|
|
87
|
+
inline.push({ name: f.name, bytes: new Uint8Array(await (await nfsp.readFile(f.node_id)).arrayBuffer()) });
|
|
70
88
|
}
|
|
71
|
-
return
|
|
89
|
+
return packBuildFiles(inline, upload, byRef);
|
|
72
90
|
}
|
|
73
|
-
const COMPONENT_KEYS = ["manifest_version", "ident", "kind", "title", "description", "host_api", "accepts", "allow_no_source", "default_size", "config_schema", "bindings", "entry", "assets", "catalog", "agent", "deps"];
|
|
91
|
+
const COMPONENT_KEYS = ["manifest_version", "ident", "kind", "title", "description", "host_api", "accepts", "allow_no_source", "default_size", "config_schema", "bindings", "entry", "assets", "catalog", "agent", "deps", "events"];
|
|
74
92
|
const SCHEMA_KEYWORDS = ["type", "enum", "default", "required", "title", "description", "minimum", "maximum", "minLength", "maxLength", "properties", "items"];
|
|
75
93
|
const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
76
94
|
const DID = /^did:[a-z0-9]+:[^/#\s]+$/;
|
|
77
95
|
const BINDING = /^[a-z][a-z0-9_]{0,31}$/;
|
|
96
|
+
const IDENT = /^did:[a-z0-9]+:[^/#\s]+#[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
97
|
+
const V2_HINT = "needs manifest_version 2 (raise it in component.json)";
|
|
98
|
+
const V3_HINT = "needs manifest_version 3 (raise it in component.json)";
|
|
99
|
+
const EVENT_PARAM_TYPES = ["string", "number", "boolean", "object", "entity"];
|
|
100
|
+
const ITEM_KINDS = ["text", "value", "metric", "bar", "list", "badge"];
|
|
101
|
+
const ITEM_KEYS = ["kind", "text", "label", "bind", "by", "format", "limit"];
|
|
102
|
+
const ACTION_COMMANDS = ["open_source", "activate_editor", "open_definition"];
|
|
103
|
+
const INSPECTOR_KINDS = ["text", "number", "select", "boolean", "color"];
|
|
104
|
+
function isRecord(v) {
|
|
105
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
106
|
+
}
|
|
107
|
+
function isSourcePath(name) {
|
|
108
|
+
return name.length > 0 && encoder.encode(name).length <= 255 && !name.includes("\0") && name.split("/").every((seg) => seg.length > 0 && !seg.startsWith("."));
|
|
109
|
+
}
|
|
110
|
+
function configProps(configSchema) {
|
|
111
|
+
const props = isRecord(configSchema) ? configSchema.properties : void 0;
|
|
112
|
+
return isRecord(props) ? props : {};
|
|
113
|
+
}
|
|
114
|
+
function configPlaceholders(text, at, props, error) {
|
|
115
|
+
for (const m of text.matchAll(/\{\{([^}]*)\}\}/g)) {
|
|
116
|
+
const inner = m[1].trim();
|
|
117
|
+
if (!inner.startsWith("config."))
|
|
118
|
+
error(`${at}: only {{config.<key>}} may be used here`);
|
|
119
|
+
else if (!(inner.slice(7) in props))
|
|
120
|
+
error(`${at}: {{${inner}}} names no property of config_schema`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function eventProblems(v, out) {
|
|
124
|
+
if (!isRecord(v)) {
|
|
125
|
+
out.push("events must be an object { <name>: { title?, description?, params? } }");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (Object.keys(v).length > 16)
|
|
129
|
+
out.push("events: at most 16");
|
|
130
|
+
for (const [name, raw] of Object.entries(v)) {
|
|
131
|
+
const at = `events.${name}`;
|
|
132
|
+
if (!BINDING.test(name))
|
|
133
|
+
out.push(`${at}: an event name is [a-z][a-z0-9_]{0,31}`);
|
|
134
|
+
if (!isRecord(raw)) {
|
|
135
|
+
out.push(`${at} must be an object { title?, description?, params? }`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
for (const [k, x] of Object.entries(raw)) {
|
|
139
|
+
if (k === "title") {
|
|
140
|
+
if (typeof x !== "string" || x.length > 256)
|
|
141
|
+
out.push(`${at}.title: at most 256 characters`);
|
|
142
|
+
} else if (k === "description") {
|
|
143
|
+
if (typeof x !== "string" || x.length > 4e3)
|
|
144
|
+
out.push(`${at}.description: at most 4000 characters`);
|
|
145
|
+
} else if (k !== "params")
|
|
146
|
+
out.push(`${at}: unknown key ${k} (title, description, params)`);
|
|
147
|
+
}
|
|
148
|
+
if (raw.params === void 0)
|
|
149
|
+
continue;
|
|
150
|
+
if (!isRecord(raw.params)) {
|
|
151
|
+
out.push(`${at}.params must be an object { <param>: { type, required?, description? } }`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (Object.keys(raw.params).length > 16)
|
|
155
|
+
out.push(`${at}.params: at most 16`);
|
|
156
|
+
for (const [p, spec] of Object.entries(raw.params)) {
|
|
157
|
+
const pat = `${at}.params.${p}`;
|
|
158
|
+
if (!BINDING.test(p))
|
|
159
|
+
out.push(`${pat}: a param name is [a-z][a-z0-9_]{0,31}`);
|
|
160
|
+
if (!isRecord(spec)) {
|
|
161
|
+
out.push(`${pat} must be an object { type, required?, description? }`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
for (const [k, x] of Object.entries(spec)) {
|
|
165
|
+
if (k === "type") {
|
|
166
|
+
if (typeof x !== "string" || !EVENT_PARAM_TYPES.includes(x))
|
|
167
|
+
out.push(`${pat}.type: ${EVENT_PARAM_TYPES.join(", ")}`);
|
|
168
|
+
} else if (k === "required") {
|
|
169
|
+
if (typeof x !== "boolean")
|
|
170
|
+
out.push(`${pat}.required must be a boolean`);
|
|
171
|
+
} else if (k === "description") {
|
|
172
|
+
if (typeof x !== "string" || x.length > 1e3)
|
|
173
|
+
out.push(`${pat}.description: at most 1000 characters`);
|
|
174
|
+
} else
|
|
175
|
+
out.push(`${pat}: unknown key ${k} (type, required, description)`);
|
|
176
|
+
}
|
|
177
|
+
if (spec.type === void 0)
|
|
178
|
+
out.push(`${pat}.type required`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
78
182
|
function schemaProblems(v, path, out) {
|
|
79
183
|
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
80
184
|
out.push(`${path}: a schema is an object`);
|
|
@@ -92,7 +196,347 @@ function schemaProblems(v, path, out) {
|
|
|
92
196
|
schemaProblems(x, `${path}/items`, out);
|
|
93
197
|
}
|
|
94
198
|
}
|
|
95
|
-
function
|
|
199
|
+
function checkDeclarative(text, context = {}, file = "declarative.json") {
|
|
200
|
+
const out = [];
|
|
201
|
+
const error = (message, code) => out.push({ file, severity: "error", message, ...code ? { code } : {} });
|
|
202
|
+
const warn = (message) => out.push({ file, severity: "warning", message });
|
|
203
|
+
let o;
|
|
204
|
+
try {
|
|
205
|
+
const parsed = JSON.parse(text);
|
|
206
|
+
if (!isRecord(parsed)) {
|
|
207
|
+
error("a declarative definition is a JSON object");
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
o = parsed;
|
|
211
|
+
} catch (e) {
|
|
212
|
+
error(e instanceof Error ? e.message : String(e));
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
const bindings = isRecord(context.bindings) ? context.bindings : {};
|
|
216
|
+
const props = configProps(context.configSchema);
|
|
217
|
+
const strings = (v, at) => {
|
|
218
|
+
if (typeof v === "string")
|
|
219
|
+
configPlaceholders(v, at, props, error);
|
|
220
|
+
else if (Array.isArray(v))
|
|
221
|
+
v.forEach((x, i) => strings(x, `${at}[${i}]`));
|
|
222
|
+
else if (isRecord(v))
|
|
223
|
+
for (const [k, x] of Object.entries(v))
|
|
224
|
+
strings(x, `${at}.${k}`);
|
|
225
|
+
};
|
|
226
|
+
const button = (it, at) => {
|
|
227
|
+
if (context.manifestVersion !== void 0 && context.manifestVersion < 3)
|
|
228
|
+
error(`${at}: a button ${V3_HINT}`);
|
|
229
|
+
for (const k of Object.keys(it))
|
|
230
|
+
if (!["kind", "label", "emit", "params"].includes(k))
|
|
231
|
+
warn(`${at}: key ${k} is ignored by the interpreter`);
|
|
232
|
+
if (typeof it.label !== "string" || !it.label)
|
|
233
|
+
error(`${at}.label: the button's text (required)`);
|
|
234
|
+
if (typeof it.emit !== "string" || !it.emit)
|
|
235
|
+
error(`${at}.emit: the event the button raises (required)`);
|
|
236
|
+
else if (context.events && !context.events.includes(it.emit))
|
|
237
|
+
error(`${at}.emit ${it.emit}: not an event of component.json`, "EVENT_UNDECLARED");
|
|
238
|
+
if (it.params !== void 0) {
|
|
239
|
+
if (isRecord(it.params))
|
|
240
|
+
strings(it.params, `${at}.params`);
|
|
241
|
+
else
|
|
242
|
+
error(`${at}.params must be an object`);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const items = (list, at) => {
|
|
246
|
+
if (list === void 0)
|
|
247
|
+
return;
|
|
248
|
+
if (!Array.isArray(list)) {
|
|
249
|
+
error(`${at} must be an array`);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
list.forEach((it, i) => {
|
|
253
|
+
const iat = `${at}[${i}]`;
|
|
254
|
+
if (!isRecord(it)) {
|
|
255
|
+
error(`${iat} must be an object`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (it.kind === "button") {
|
|
259
|
+
button(it, iat);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (typeof it.kind !== "string" || !ITEM_KINDS.includes(it.kind))
|
|
263
|
+
error(`${iat}.kind ${JSON.stringify(it.kind)}: one of ${[...ITEM_KINDS, "button"].join(", ")}`);
|
|
264
|
+
for (const k of Object.keys(it))
|
|
265
|
+
if (!ITEM_KEYS.includes(k))
|
|
266
|
+
warn(`${iat}: key ${k} is ignored by the interpreter`);
|
|
267
|
+
if (it.format !== void 0 && !["text", "number", "percent", "currency"].includes(it.format))
|
|
268
|
+
error(`${iat}.format ${JSON.stringify(it.format)}: text, number, percent or currency`);
|
|
269
|
+
if (typeof it.bind === "string" && it.bind.includes(".")) {
|
|
270
|
+
const prefix = it.bind.slice(0, it.bind.indexOf("."));
|
|
271
|
+
if (prefix !== "props" && !(prefix in bindings))
|
|
272
|
+
warn(`${iat}.bind ${JSON.stringify(it.bind)}: ${prefix} is not a declared binding, so it reads the field ${JSON.stringify(it.bind)} of the main source`);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
for (const [k, v] of Object.entries(o)) {
|
|
277
|
+
if (k === "layout") {
|
|
278
|
+
if (v !== "stack" && v !== "row")
|
|
279
|
+
error("layout: stack or row");
|
|
280
|
+
} else if (k === "items")
|
|
281
|
+
items(v, "items");
|
|
282
|
+
else if (k === "view")
|
|
283
|
+
items(isRecord(v) ? v.items : void 0, "view.items");
|
|
284
|
+
else if (k === "accent") {
|
|
285
|
+
if (typeof v !== "string")
|
|
286
|
+
error("accent must be a colour string");
|
|
287
|
+
} else if (k === "actions") {
|
|
288
|
+
(Array.isArray(v) ? v : []).forEach((a, i) => {
|
|
289
|
+
const r = isRecord(a) ? a : {};
|
|
290
|
+
if (typeof r.command !== "string" || !ACTION_COMMANDS.includes(r.command))
|
|
291
|
+
error(`actions[${i}].command: one of ${ACTION_COMMANDS.join(", ")}`);
|
|
292
|
+
if (typeof r.id !== "string" || typeof r.label !== "string")
|
|
293
|
+
error(`actions[${i}] needs id and label`);
|
|
294
|
+
});
|
|
295
|
+
} else if (k === "inspector") {
|
|
296
|
+
(Array.isArray(v) ? v : []).forEach((f, i) => {
|
|
297
|
+
const r = isRecord(f) ? f : {};
|
|
298
|
+
if (typeof r.kind !== "string" || !INSPECTOR_KINDS.includes(r.kind))
|
|
299
|
+
error(`inspector[${i}].kind: one of ${INSPECTOR_KINDS.join(", ")}`);
|
|
300
|
+
if (typeof r.key !== "string" || typeof r.label !== "string")
|
|
301
|
+
error(`inspector[${i}] needs key and label`);
|
|
302
|
+
});
|
|
303
|
+
} else
|
|
304
|
+
warn(`key ${k} is not part of the declarative vocabulary`);
|
|
305
|
+
}
|
|
306
|
+
if (o.items === void 0 && o.view === void 0)
|
|
307
|
+
warn("no items: the control draws nothing");
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
function checkComposition(text, configSchema, file = AIWS_COMPOSITION_FILE, context = {}) {
|
|
311
|
+
const out = [];
|
|
312
|
+
const error = (message, code) => out.push({ file, severity: "error", message, ...code ? { code } : {} });
|
|
313
|
+
let o;
|
|
314
|
+
try {
|
|
315
|
+
const parsed = JSON.parse(text);
|
|
316
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
317
|
+
error("a composition is a JSON object { layout, slots, chrome?, events? }");
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
o = parsed;
|
|
321
|
+
} catch (e) {
|
|
322
|
+
error(e instanceof Error ? e.message : String(e));
|
|
323
|
+
return out;
|
|
324
|
+
}
|
|
325
|
+
const props = configProps(configSchema);
|
|
326
|
+
const placeholders = (t, at) => configPlaceholders(t, at, props, error);
|
|
327
|
+
const dimension = (v, at, max) => {
|
|
328
|
+
if (typeof v === "number" && v >= 0 && v <= max)
|
|
329
|
+
return;
|
|
330
|
+
if (typeof v === "string" && /^\s*\{\{.*\}\}\s*$/.test(v)) {
|
|
331
|
+
placeholders(v, at);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
error(`${at}: a number in [0, ${max}] or {{config.<key>}}`);
|
|
335
|
+
};
|
|
336
|
+
for (const k of Object.keys(o)) {
|
|
337
|
+
if (!["layout", "slots", "chrome", "events"].includes(k))
|
|
338
|
+
error(`unknown key ${k} (a composition says layout, slots, chrome and events)`);
|
|
339
|
+
}
|
|
340
|
+
const layout = o.layout;
|
|
341
|
+
let algorithm = "stack";
|
|
342
|
+
if (!layout || typeof layout !== "object")
|
|
343
|
+
error("layout { algorithm: stack | fixed | grid } required");
|
|
344
|
+
else {
|
|
345
|
+
if (layout.algorithm === "stack" || layout.algorithm === "fixed" || layout.algorithm === "grid")
|
|
346
|
+
algorithm = layout.algorithm;
|
|
347
|
+
else
|
|
348
|
+
error(`layout.algorithm ${JSON.stringify(layout.algorithm)}: stack, fixed or grid (the algorithms the host ships)`);
|
|
349
|
+
for (const [k, v] of Object.entries(layout)) {
|
|
350
|
+
if (k === "algorithm")
|
|
351
|
+
continue;
|
|
352
|
+
if ((k === "direction" || k === "spacing") && algorithm !== "stack") {
|
|
353
|
+
error(`layout.${k} belongs to the stack layout`);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if ((k === "columns" || k === "gap") && algorithm !== "grid") {
|
|
357
|
+
error(`layout.${k} belongs to the grid layout`);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (k === "direction") {
|
|
361
|
+
if (v !== "column" && v !== "row")
|
|
362
|
+
error("layout.direction: column or row");
|
|
363
|
+
} else if (k === "spacing")
|
|
364
|
+
dimension(v, "layout.spacing", 2e3);
|
|
365
|
+
else if (k === "columns") {
|
|
366
|
+
if (!Number.isInteger(v) || v < 1 || v > 12)
|
|
367
|
+
error("layout.columns: an integer in [1, 12]");
|
|
368
|
+
} else if (k === "gap")
|
|
369
|
+
dimension(v, "layout.gap", 2e3);
|
|
370
|
+
else if (k === "padding") {
|
|
371
|
+
if (v && typeof v === "object" && !Array.isArray(v))
|
|
372
|
+
for (const [side, n] of Object.entries(v)) {
|
|
373
|
+
if (!["t", "r", "b", "l"].includes(side))
|
|
374
|
+
error(`layout.padding.${side}: t, r, b or l`);
|
|
375
|
+
dimension(n, `layout.padding.${side}`, 2e3);
|
|
376
|
+
}
|
|
377
|
+
else
|
|
378
|
+
dimension(v, "layout.padding", 2e3);
|
|
379
|
+
} else
|
|
380
|
+
error(`layout: unknown key ${k}`);
|
|
381
|
+
}
|
|
382
|
+
if (algorithm === "grid" && layout.columns === void 0)
|
|
383
|
+
error("layout.columns (1…12) required: the cells of a grid row");
|
|
384
|
+
}
|
|
385
|
+
const slots = o.slots && typeof o.slots === "object" && !Array.isArray(o.slots) ? o.slots : null;
|
|
386
|
+
if (!slots)
|
|
387
|
+
error("slots { <name>: { accepts?, min?, max?, placement? } } required");
|
|
388
|
+
else {
|
|
389
|
+
if (Object.keys(slots).length > 16)
|
|
390
|
+
error("at most 16 slots");
|
|
391
|
+
for (const [name, raw] of Object.entries(slots)) {
|
|
392
|
+
const at = `slots.${name}`;
|
|
393
|
+
if (!BINDING.test(name))
|
|
394
|
+
error(`${at}: a slot name is [a-z][a-z0-9_]{0,31}`);
|
|
395
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
396
|
+
error(`${at} must be an object`);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const slot = raw;
|
|
400
|
+
for (const [k, v] of Object.entries(slot)) {
|
|
401
|
+
if (k === "accepts") {
|
|
402
|
+
if (!Array.isArray(v) || v.length === 0 || v.length > 32) {
|
|
403
|
+
error(`${at}.accepts: 1…32 entries (cell:<ident>, a view type, container)`);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
for (const a of v) {
|
|
407
|
+
if (typeof a === "string" && a.startsWith("cell:")) {
|
|
408
|
+
if (!IDENT.test(a.slice(5)) || encoder.encode(a.slice(5)).length > 128)
|
|
409
|
+
error(`${at}.accepts ${JSON.stringify(a)}: cell:<author_did>#<name>`);
|
|
410
|
+
} else if (a !== "container" && !(typeof a === "string" && NAME.test(a)))
|
|
411
|
+
error(`${at}.accepts ${JSON.stringify(a)}: cell:<ident>, a view type or container`);
|
|
412
|
+
}
|
|
413
|
+
} else if (k === "min") {
|
|
414
|
+
if (!Number.isInteger(v) || v < 0 || v > 64)
|
|
415
|
+
error(`${at}.min: an integer in [0, 64]`);
|
|
416
|
+
} else if (k === "max") {
|
|
417
|
+
if (!Number.isInteger(v) || v < 1 || v > 1e3)
|
|
418
|
+
error(`${at}.max: an integer in [1, 1000]`);
|
|
419
|
+
} else if (k === "placement") {
|
|
420
|
+
if (v !== "stack" && v !== "top" && v !== "bottom" && v !== "free")
|
|
421
|
+
error(`${at}.placement: stack, top, bottom or free`);
|
|
422
|
+
else if (algorithm === "fixed" && v !== "free")
|
|
423
|
+
error(`${at}.placement: a fixed layout keeps every child where it is (free)`);
|
|
424
|
+
else if (algorithm === "grid" && v !== "stack")
|
|
425
|
+
error(`${at}.placement: a grid fills its cells with the slots' children in slot order (stack)`);
|
|
426
|
+
} else if (k === "title") {
|
|
427
|
+
if (typeof v !== "string" || v.length > 256)
|
|
428
|
+
error(`${at}.title: at most 256 characters`);
|
|
429
|
+
} else
|
|
430
|
+
error(`${at}: unknown key ${k}`);
|
|
431
|
+
}
|
|
432
|
+
if (Number.isInteger(slot.min) && Number.isInteger(slot.max) && slot.min > slot.max)
|
|
433
|
+
error(`${at}: min exceeds max`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const chrome = o.chrome;
|
|
437
|
+
if (chrome !== void 0) {
|
|
438
|
+
if (!chrome || typeof chrome !== "object" || Array.isArray(chrome))
|
|
439
|
+
error("chrome must be an object");
|
|
440
|
+
else
|
|
441
|
+
for (const [k, v] of Object.entries(chrome)) {
|
|
442
|
+
const at = `chrome.${k}`;
|
|
443
|
+
if (k === "background" || k === "border") {
|
|
444
|
+
if (typeof v === "string" && v.length <= 64)
|
|
445
|
+
placeholders(v, at);
|
|
446
|
+
else
|
|
447
|
+
error(`${at}: a colour (≤ 64 characters) or {{config.<key>}}`);
|
|
448
|
+
} else if (k === "radius")
|
|
449
|
+
dimension(v, at, 200);
|
|
450
|
+
else if (k === "empty") {
|
|
451
|
+
if (typeof v === "string" && v.length <= 1e3)
|
|
452
|
+
placeholders(v, at);
|
|
453
|
+
else
|
|
454
|
+
error(`${at}: a text of at most 1000 characters`);
|
|
455
|
+
} else if (k === "title") {
|
|
456
|
+
const t = v;
|
|
457
|
+
if (!t || typeof t !== "object" || Array.isArray(t)) {
|
|
458
|
+
error(`${at}: { text, slot? }`);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
for (const [tk, tv] of Object.entries(t)) {
|
|
462
|
+
if (tk === "text") {
|
|
463
|
+
if (typeof tv === "string" && tv.length <= 256)
|
|
464
|
+
placeholders(tv, "chrome.title.text");
|
|
465
|
+
else
|
|
466
|
+
error("chrome.title.text: at most 256 characters");
|
|
467
|
+
} else if (tk === "slot") {
|
|
468
|
+
if (typeof tv !== "string" || !slots || !(tv in slots))
|
|
469
|
+
error(`chrome.title.slot ${JSON.stringify(tv)}: a slot of this composition`);
|
|
470
|
+
} else
|
|
471
|
+
error(`chrome.title: unknown key ${tk}`);
|
|
472
|
+
}
|
|
473
|
+
if (typeof t.text !== "string")
|
|
474
|
+
error("chrome.title.text required");
|
|
475
|
+
} else
|
|
476
|
+
error(`chrome: unknown key ${k} (background, radius, border, title, empty)`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (o.events !== void 0) {
|
|
480
|
+
if (context.manifestVersion !== void 0 && context.manifestVersion < 3)
|
|
481
|
+
error(`events ${V3_HINT}`);
|
|
482
|
+
if (!isRecord(o.events))
|
|
483
|
+
error("events must be an object { <name>: { from: { slot, event }, title?, description? } }");
|
|
484
|
+
else {
|
|
485
|
+
if (Object.keys(o.events).length > 16)
|
|
486
|
+
error("events: at most 16");
|
|
487
|
+
for (const [name, raw] of Object.entries(o.events)) {
|
|
488
|
+
const at = `events.${name}`;
|
|
489
|
+
if (!BINDING.test(name))
|
|
490
|
+
error(`${at}: an event name is [a-z][a-z0-9_]{0,31}`);
|
|
491
|
+
if (!isRecord(raw)) {
|
|
492
|
+
error(`${at} must be an object { from: { slot, event }, title?, description? }`);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
496
|
+
if (k === "title") {
|
|
497
|
+
if (typeof v !== "string" || v.length > 256)
|
|
498
|
+
error(`${at}.title: at most 256 characters`);
|
|
499
|
+
} else if (k === "description") {
|
|
500
|
+
if (typeof v !== "string" || v.length > 4e3)
|
|
501
|
+
error(`${at}.description: at most 4000 characters`);
|
|
502
|
+
} else if (k !== "from")
|
|
503
|
+
error(`${at}: unknown key ${k} (from, title, description)`);
|
|
504
|
+
}
|
|
505
|
+
const from = raw.from;
|
|
506
|
+
if (!isRecord(from)) {
|
|
507
|
+
error(`${at}.from { slot, event } required: the child event it re-raises`);
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
for (const k of Object.keys(from))
|
|
511
|
+
if (k !== "slot" && k !== "event")
|
|
512
|
+
error(`${at}.from: unknown key ${k} (slot, event)`);
|
|
513
|
+
const event = typeof from.event === "string" && BINDING.test(from.event) ? from.event : void 0;
|
|
514
|
+
if (!event)
|
|
515
|
+
error(`${at}.from.event: an event name [a-z][a-z0-9_]{0,31}`);
|
|
516
|
+
const slot = typeof from.slot === "string" && slots ? slots[from.slot] : void 0;
|
|
517
|
+
if (!isRecord(slot)) {
|
|
518
|
+
error(`${at}.from.slot ${JSON.stringify(from.slot)}: a slot of this composition`);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (!event)
|
|
522
|
+
continue;
|
|
523
|
+
const cells = (Array.isArray(slot.accepts) ? slot.accepts : []).filter((a) => typeof a === "string" && a.startsWith("cell:")).map((a) => a.slice(5));
|
|
524
|
+
if (cells.length === 0) {
|
|
525
|
+
error(`${at}: slot ${from.slot} accepts no cell:<ident>, so no control there declares ${event}`, "EVENT_UNDECLARED");
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const declared = cells.map((ident) => {
|
|
529
|
+
var _a;
|
|
530
|
+
return (_a = context.cellEvents) == null ? void 0 : _a.call(context, ident);
|
|
531
|
+
});
|
|
532
|
+
if (declared.every((names) => names !== void 0 && !names.includes(event)))
|
|
533
|
+
error(`${at}: no control slot ${from.slot} accepts (${cells.join(", ")}) declares the event ${event}`, "EVENT_UNDECLARED");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return out;
|
|
538
|
+
}
|
|
539
|
+
function checkComponent(text, fileNames, readText, cellEvents) {
|
|
96
540
|
const out = [];
|
|
97
541
|
const f = AIWS_COMPONENT_FILE;
|
|
98
542
|
const error = (message) => out.push({ file: f, severity: "error", message });
|
|
@@ -112,11 +556,14 @@ function checkComponent(text, fileNames) {
|
|
|
112
556
|
if (!COMPONENT_KEYS.includes(k))
|
|
113
557
|
error(`unknown key ${k}`);
|
|
114
558
|
const mv = c.manifest_version;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
559
|
+
const version = typeof mv === "number" && Number.isInteger(mv) && mv >= 1 ? mv : 0;
|
|
560
|
+
if (!version)
|
|
561
|
+
error(`manifest_version (1…${AIWS_MANIFEST_VERSION}) required`);
|
|
562
|
+
else if (version > AIWS_MANIFEST_VERSION)
|
|
118
563
|
error(`manifest_version ${mv} is newer than this client reads (${AIWS_MANIFEST_VERSION})`);
|
|
564
|
+
const before3 = version > 0 && version < 3;
|
|
119
565
|
const ident = c.ident;
|
|
566
|
+
let self = "";
|
|
120
567
|
if (!ident || typeof ident !== "object")
|
|
121
568
|
error("ident { author_did, name } required");
|
|
122
569
|
else {
|
|
@@ -124,11 +571,22 @@ function checkComponent(text, fileNames) {
|
|
|
124
571
|
error("ident.author_did must be a DID (did:<method>:<id>)");
|
|
125
572
|
if (typeof ident.name !== "string" || !NAME.test(ident.name))
|
|
126
573
|
error("ident.name must be [a-z0-9][a-z0-9._-]{0,63}");
|
|
127
|
-
if (typeof ident.author_did === "string" && typeof ident.name === "string"
|
|
128
|
-
|
|
574
|
+
if (typeof ident.author_did === "string" && typeof ident.name === "string") {
|
|
575
|
+
self = `${ident.author_did}#${ident.name}`;
|
|
576
|
+
if (encoder.encode(self).length > 128)
|
|
577
|
+
error("an identity is at most 128 bytes");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
const composite = c.kind === "composite";
|
|
581
|
+
if (c.kind !== "declarative" && c.kind !== "html" && !composite)
|
|
582
|
+
error("kind must be declarative, html or composite");
|
|
583
|
+
if (composite && version === 1)
|
|
584
|
+
error(`kind composite ${V2_HINT}`);
|
|
585
|
+
if (composite) {
|
|
586
|
+
for (const k of ["accepts", "allow_no_source"])
|
|
587
|
+
if (c[k] !== void 0)
|
|
588
|
+
error(`${k}: a composite is a container of Blocks, not a Cell showing data`);
|
|
129
589
|
}
|
|
130
|
-
if (c.kind !== "declarative" && c.kind !== "html")
|
|
131
|
-
error("kind must be declarative or html");
|
|
132
590
|
if (typeof c.title !== "string" || !c.title || c.title.length > 256)
|
|
133
591
|
error("title: 1…256 characters");
|
|
134
592
|
if (c.config_schema !== void 0) {
|
|
@@ -144,8 +602,38 @@ function checkComponent(text, fileNames) {
|
|
|
144
602
|
if (!BINDING.test(n) || n === "props")
|
|
145
603
|
error(`binding name ${n}: [a-z][a-z0-9_]{0,31}, not props`);
|
|
146
604
|
}
|
|
147
|
-
if (
|
|
148
|
-
|
|
605
|
+
if (c.deps !== void 0) {
|
|
606
|
+
if (!Array.isArray(c.deps) || c.deps.length > 32)
|
|
607
|
+
error("deps: at most 32 identities <author_did>#<name>");
|
|
608
|
+
else if (c.deps.length > 0 && version === 1)
|
|
609
|
+
error(`deps ${V2_HINT}`);
|
|
610
|
+
else {
|
|
611
|
+
const seen = /* @__PURE__ */ new Set();
|
|
612
|
+
for (const d of c.deps) {
|
|
613
|
+
if (typeof d !== "string" || !IDENT.test(d))
|
|
614
|
+
error(`deps: ${JSON.stringify(d)} is not an identity <author_did>#<name>`);
|
|
615
|
+
else if (d === self)
|
|
616
|
+
error("deps: a control cannot depend on itself");
|
|
617
|
+
else if (seen.has(d))
|
|
618
|
+
error(`deps lists ${d} twice`);
|
|
619
|
+
seen.add(String(d));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
let events = [];
|
|
624
|
+
if (c.events !== void 0) {
|
|
625
|
+
if (composite)
|
|
626
|
+
error("events: a composite declares its events in composition.json (events.<name>.from re-raises an event of a child)");
|
|
627
|
+
else if (before3)
|
|
628
|
+
error(`events ${V3_HINT}`);
|
|
629
|
+
else {
|
|
630
|
+
const p = [];
|
|
631
|
+
eventProblems(c.events, p);
|
|
632
|
+
p.forEach(error);
|
|
633
|
+
}
|
|
634
|
+
if (!composite && isRecord(c.events))
|
|
635
|
+
events = Object.keys(c.events);
|
|
636
|
+
}
|
|
149
637
|
const has = (n) => typeof n === "string" && fileNames.includes(n);
|
|
150
638
|
const named = /* @__PURE__ */ new Set([f]);
|
|
151
639
|
if (c.kind === "html") {
|
|
@@ -159,13 +647,28 @@ function checkComponent(text, fileNames) {
|
|
|
159
647
|
else
|
|
160
648
|
named.add(String(n));
|
|
161
649
|
}
|
|
162
|
-
if (c.host_api !== void 0 && c.host_api !==
|
|
163
|
-
error(
|
|
164
|
-
|
|
650
|
+
if (c.host_api !== void 0 && c.host_api !== 2 && c.host_api !== 3)
|
|
651
|
+
error("host_api: the HTML Block host speaks API 2 and 3");
|
|
652
|
+
else if (c.host_api === 3 && before3)
|
|
653
|
+
error(`host_api 3 ${V3_HINT}`);
|
|
654
|
+
if (events.length > 0 && c.host_api !== 3)
|
|
655
|
+
out.push({ file: f, severity: "warning", message: "events: an HTML control raises them with aiws.emit, which is host API 3 (set host_api to 3)" });
|
|
656
|
+
} else if (c.kind === "declarative" || composite) {
|
|
165
657
|
if (!has(c.entry))
|
|
166
658
|
error(`entry ${JSON.stringify(c.entry)} is not in the directory`);
|
|
167
|
-
else
|
|
659
|
+
else {
|
|
168
660
|
named.add(String(c.entry));
|
|
661
|
+
const entryText = readText == null ? void 0 : readText(String(c.entry));
|
|
662
|
+
const manifestVersion = version || void 0;
|
|
663
|
+
if (entryText !== void 0 && composite)
|
|
664
|
+
out.push(...checkComposition(entryText, c.config_schema, String(c.entry), { manifestVersion, cellEvents }));
|
|
665
|
+
else if (entryText !== void 0)
|
|
666
|
+
out.push(...checkDeclarative(entryText, { bindings: c.bindings, events, configSchema: c.config_schema, manifestVersion }, String(c.entry)));
|
|
667
|
+
}
|
|
668
|
+
if (composite && c.host_api !== void 0)
|
|
669
|
+
error("host_api: a composite runs no code of its own (its layout and chrome are the host's)");
|
|
670
|
+
if (!composite && c.host_api !== void 0 && c.host_api !== AIWS_DECLARATIVE_API_VERSION)
|
|
671
|
+
error(`host_api: the declarative interpreter is version ${AIWS_DECLARATIVE_API_VERSION}`);
|
|
169
672
|
}
|
|
170
673
|
for (const a of Array.isArray(c.assets) ? c.assets : []) {
|
|
171
674
|
if (!has(a))
|
|
@@ -179,11 +682,22 @@ function checkComponent(text, fileNames) {
|
|
|
179
682
|
else
|
|
180
683
|
named.add(String(c.agent));
|
|
181
684
|
}
|
|
182
|
-
for (const n of fileNames)
|
|
183
|
-
if (!
|
|
685
|
+
for (const n of fileNames) {
|
|
686
|
+
if (!isSourcePath(n))
|
|
687
|
+
out.push({ file: n, severity: "error", message: "a file of a source directory: a relative path whose parts do not start with '.'" });
|
|
688
|
+
else if (!named.has(n))
|
|
184
689
|
out.push({ file: n, severity: "warning", message: "not named by component.json: kept in the source snapshot only" });
|
|
690
|
+
}
|
|
185
691
|
return out;
|
|
186
692
|
}
|
|
693
|
+
function slotCells(composition) {
|
|
694
|
+
const out = /* @__PURE__ */ new Set();
|
|
695
|
+
for (const slot of Object.values(composition.slots ?? {}))
|
|
696
|
+
for (const a of slot.accepts ?? [])
|
|
697
|
+
if (a.startsWith("cell:"))
|
|
698
|
+
out.add(a.slice(5));
|
|
699
|
+
return [...out].sort();
|
|
700
|
+
}
|
|
187
701
|
function compatManifest(value) {
|
|
188
702
|
if (!value || typeof value !== "object")
|
|
189
703
|
throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "must be a JSON object" }]);
|
|
@@ -199,6 +713,101 @@ function compatManifest(value) {
|
|
|
199
713
|
throw new AiwsSourceError([{ file: AIWS_MANIFEST_FILE, severity: "error", message: "controls must be an array" }]);
|
|
200
714
|
return { manifest_version: version, purpose: "aiws", host_api: m.host_api ?? {}, controls: m.controls, workspace_templates: Array.isArray(m.workspace_templates) ? m.workspace_templates : [] };
|
|
201
715
|
}
|
|
716
|
+
const TEMPLATE_ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
717
|
+
function templateDir(id) {
|
|
718
|
+
return `templates/${id}/`;
|
|
719
|
+
}
|
|
720
|
+
async function aiwsTemplateDigest(files, id) {
|
|
721
|
+
const dir = templateDir(id);
|
|
722
|
+
const own = [];
|
|
723
|
+
for (const [path, bytes] of files)
|
|
724
|
+
if (path.startsWith(dir))
|
|
725
|
+
own.push([path.slice(dir.length), bytes]);
|
|
726
|
+
return aiwsContentDigest(own);
|
|
727
|
+
}
|
|
728
|
+
function templateProblems(t, controls) {
|
|
729
|
+
if (!isRecord(t))
|
|
730
|
+
return ["a template is an object { id, title, snapshot, digest, requires, description?, params?, reset? }"];
|
|
731
|
+
const out = [];
|
|
732
|
+
for (const k of Object.keys(t))
|
|
733
|
+
if (!["id", "title", "description", "snapshot", "digest", "requires", "params", "reset"].includes(k))
|
|
734
|
+
out.push(`unknown key ${k}`);
|
|
735
|
+
if (typeof t.id !== "string" || !TEMPLATE_ID.test(t.id))
|
|
736
|
+
out.push("id: [a-z0-9][a-z0-9-]{0,63}");
|
|
737
|
+
else if (t.snapshot !== `${templateDir(t.id)}snapshot.json`)
|
|
738
|
+
out.push(`snapshot must be ${templateDir(t.id)}snapshot.json`);
|
|
739
|
+
if (typeof t.title !== "string" || !t.title || t.title.length > 256)
|
|
740
|
+
out.push("title: 1…256 characters");
|
|
741
|
+
if (t.description !== void 0 && (typeof t.description !== "string" || t.description.length > 4e3))
|
|
742
|
+
out.push("description: at most 4000 characters");
|
|
743
|
+
if (typeof t.digest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(t.digest))
|
|
744
|
+
out.push("digest: sha256:<64 hex digits>");
|
|
745
|
+
const requires = t.requires;
|
|
746
|
+
if (!isRecord(requires))
|
|
747
|
+
out.push("requires { host_api?, controls } required");
|
|
748
|
+
else {
|
|
749
|
+
for (const k of Object.keys(requires))
|
|
750
|
+
if (k !== "host_api" && k !== "controls")
|
|
751
|
+
out.push(`requires: unknown key ${k}`);
|
|
752
|
+
if (requires.host_api !== void 0) {
|
|
753
|
+
if (!isRecord(requires.host_api))
|
|
754
|
+
out.push("requires.host_api must be an object { html?, declarative? }");
|
|
755
|
+
else
|
|
756
|
+
for (const [k, v] of Object.entries(requires.host_api))
|
|
757
|
+
if (k !== "html" && k !== "declarative" || !Number.isInteger(v) || v < 1)
|
|
758
|
+
out.push(`requires.host_api.${k}: html or declarative, a positive integer`);
|
|
759
|
+
}
|
|
760
|
+
if (!Array.isArray(requires.controls) || requires.controls.length > 256)
|
|
761
|
+
out.push("requires.controls: at most 256 identities");
|
|
762
|
+
else {
|
|
763
|
+
const seen = /* @__PURE__ */ new Set();
|
|
764
|
+
for (const ident of requires.controls) {
|
|
765
|
+
if (typeof ident !== "string" || !controls.has(ident))
|
|
766
|
+
out.push(`requires.controls: ${JSON.stringify(ident)} is not a control of this manifest`);
|
|
767
|
+
else if (seen.has(ident))
|
|
768
|
+
out.push(`requires.controls lists ${ident} twice`);
|
|
769
|
+
seen.add(ident);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (t.params !== void 0) {
|
|
774
|
+
if (!isRecord(t.params) || Object.keys(t.params).length > 32)
|
|
775
|
+
out.push("params: an object of at most 32 { type, title?, default? }");
|
|
776
|
+
else
|
|
777
|
+
for (const [name, p] of Object.entries(t.params)) {
|
|
778
|
+
const at = `params.${name}`;
|
|
779
|
+
if (!BINDING.test(name))
|
|
780
|
+
out.push(`${at}: a param name is [a-z][a-z0-9_]{0,31}`);
|
|
781
|
+
if (!isRecord(p)) {
|
|
782
|
+
out.push(`${at} must be an object { type, title?, default? }`);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
for (const [k, v] of Object.entries(p)) {
|
|
786
|
+
if (k === "type") {
|
|
787
|
+
if (v !== "string" && v !== "number" && v !== "boolean")
|
|
788
|
+
out.push(`${at}.type: string, number or boolean`);
|
|
789
|
+
} else if (k === "title") {
|
|
790
|
+
if (typeof v !== "string" || v.length > 256)
|
|
791
|
+
out.push(`${at}.title: at most 256 characters`);
|
|
792
|
+
} else if (k === "default") {
|
|
793
|
+
if (!["string", "number", "boolean"].includes(typeof v))
|
|
794
|
+
out.push(`${at}.default: a string, number or boolean`);
|
|
795
|
+
} else
|
|
796
|
+
out.push(`${at}: unknown key ${k} (type, title, default)`);
|
|
797
|
+
}
|
|
798
|
+
if (p.type === void 0)
|
|
799
|
+
out.push(`${at}.type required`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (t.reset !== void 0) {
|
|
803
|
+
const tables = isRecord(t.reset) ? t.reset.tables : void 0;
|
|
804
|
+
if (!isRecord(t.reset) || Object.keys(t.reset).some((k) => k !== "tables"))
|
|
805
|
+
out.push("reset: { tables }");
|
|
806
|
+
else if (tables !== void 0 && (!Array.isArray(tables) || tables.length > 64 || new Set(tables).size !== tables.length || tables.some((x) => typeof x !== "string" || !/^[a-z0-9][a-z0-9_-]{0,63}$/.test(x))))
|
|
807
|
+
out.push("reset.tables: at most 64 distinct table ids");
|
|
808
|
+
}
|
|
809
|
+
return out;
|
|
810
|
+
}
|
|
202
811
|
async function verifyBundle(files) {
|
|
203
812
|
var _a, _b;
|
|
204
813
|
const raw = files.get(AIWS_MANIFEST_FILE);
|
|
@@ -212,10 +821,23 @@ async function verifyBundle(files) {
|
|
|
212
821
|
}
|
|
213
822
|
const manifest = compatManifest(parsed);
|
|
214
823
|
const diagnostics = [];
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
824
|
+
const templates = manifest.workspace_templates;
|
|
825
|
+
if (templates.length > 0 && manifest.manifest_version < 3)
|
|
826
|
+
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: `Workspace templates need manifest_version 3 (raise it in ${AIWS_MANIFEST_FILE})` });
|
|
827
|
+
if (templates.length > 32)
|
|
828
|
+
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: "at most 32 Workspace templates" });
|
|
829
|
+
if (manifest.controls.length === 0 && templates.length === 0)
|
|
218
830
|
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: "lists no control" });
|
|
831
|
+
const declared = /* @__PURE__ */ new Map();
|
|
832
|
+
for (const c of manifest.controls) {
|
|
833
|
+
const component = typeof c.dir === "string" ? files.get(`${c.dir}${AIWS_COMPONENT_FILE}`) : void 0;
|
|
834
|
+
try {
|
|
835
|
+
const comp = component ? JSON.parse(decoder.decode(component)) : void 0;
|
|
836
|
+
if (isRecord(comp))
|
|
837
|
+
declared.set(c.ident, isRecord(comp.events) ? Object.keys(comp.events) : []);
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
219
841
|
const dirs = /* @__PURE__ */ new Set();
|
|
220
842
|
for (const c of manifest.controls) {
|
|
221
843
|
if (typeof c.dir !== "string" || !/^controls\/[a-z0-9][a-z0-9._-]{0,70}\/$/.test(c.dir) || dirs.has(c.dir)) {
|
|
@@ -224,8 +846,6 @@ async function verifyBundle(files) {
|
|
|
224
846
|
}
|
|
225
847
|
dirs.add(c.dir);
|
|
226
848
|
const own = [...files].filter(([p]) => p.startsWith(c.dir)).map(([p, b]) => [p.slice(c.dir.length), b]);
|
|
227
|
-
if (own.some(([n]) => n.includes("/")))
|
|
228
|
-
diagnostics.push({ file: c.dir, severity: "error", message: "a control directory is flat" });
|
|
229
849
|
if (await aiwsContentDigest(own) !== c.digest)
|
|
230
850
|
diagnostics.push({ file: c.dir, severity: "error", message: "the files do not match the manifest digest" });
|
|
231
851
|
const component = own.find(([n]) => n === AIWS_COMPONENT_FILE);
|
|
@@ -233,18 +853,55 @@ async function verifyBundle(files) {
|
|
|
233
853
|
diagnostics.push({ file: `${c.dir}${AIWS_COMPONENT_FILE}`, severity: "error", message: "missing" });
|
|
234
854
|
continue;
|
|
235
855
|
}
|
|
236
|
-
|
|
856
|
+
const text = (name) => {
|
|
857
|
+
const hit = own.find(([n]) => n === name);
|
|
858
|
+
return hit ? decoder.decode(hit[1]) : void 0;
|
|
859
|
+
};
|
|
860
|
+
for (const d of checkComponent(decoder.decode(component[1]), own.map(([n]) => n), text, (ident) => declared.get(ident)))
|
|
237
861
|
diagnostics.push({ ...d, file: `${c.dir}${d.file}` });
|
|
238
862
|
try {
|
|
239
863
|
const comp = JSON.parse(decoder.decode(component[1]));
|
|
240
864
|
if (`${(_a = comp.ident) == null ? void 0 : _a.author_did}#${(_b = comp.ident) == null ? void 0 : _b.name}` !== c.ident || comp.kind !== c.kind)
|
|
241
865
|
diagnostics.push({ file: c.dir, severity: "error", message: "ident and kind must match its component.json" });
|
|
866
|
+
if (JSON.stringify([...comp.deps ?? []].sort()) !== JSON.stringify([...c.deps ?? []].sort()))
|
|
867
|
+
diagnostics.push({ file: c.dir, severity: "error", message: "deps must match its component.json" });
|
|
242
868
|
} catch {
|
|
243
869
|
}
|
|
244
870
|
}
|
|
871
|
+
const idents = new Set(manifest.controls.map((c) => c.ident));
|
|
872
|
+
for (const c of manifest.controls)
|
|
873
|
+
for (const d of c.deps ?? [])
|
|
874
|
+
if (!idents.has(d))
|
|
875
|
+
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: `${c.ident} depends on ${d}, which the bundle does not carry` });
|
|
876
|
+
for (const [i, t] of templates.entries()) {
|
|
877
|
+
const label = isRecord(t) && typeof t.id === "string" ? `template ${t.id}` : `workspace_templates[${i}]`;
|
|
878
|
+
for (const p of templateProblems(t, idents))
|
|
879
|
+
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: `${label}: ${p}` });
|
|
880
|
+
if (!isRecord(t) || typeof t.id !== "string" || !TEMPLATE_ID.test(t.id))
|
|
881
|
+
continue;
|
|
882
|
+
const dir = templateDir(t.id);
|
|
883
|
+
if (dirs.has(dir)) {
|
|
884
|
+
diagnostics.push({ file: AIWS_MANIFEST_FILE, severity: "error", message: `${label} is listed twice` });
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
dirs.add(dir);
|
|
888
|
+
const snapshot = files.get(`${dir}snapshot.json`);
|
|
889
|
+
let format;
|
|
890
|
+
try {
|
|
891
|
+
format = snapshot ? JSON.parse(decoder.decode(snapshot)).format : void 0;
|
|
892
|
+
} catch {
|
|
893
|
+
format = void 0;
|
|
894
|
+
}
|
|
895
|
+
if (!snapshot)
|
|
896
|
+
diagnostics.push({ file: `${dir}snapshot.json`, severity: "error", message: "missing" });
|
|
897
|
+
else if (format !== AIWS_TEMPLATE_FORMAT)
|
|
898
|
+
diagnostics.push({ file: `${dir}snapshot.json`, severity: "error", message: `not a template snapshot (format ${AIWS_TEMPLATE_FORMAT})` });
|
|
899
|
+
if (await aiwsTemplateDigest(files, t.id) !== t.digest)
|
|
900
|
+
diagnostics.push({ file: dir, severity: "error", message: "the files do not match the template digest" });
|
|
901
|
+
}
|
|
245
902
|
for (const p of files.keys())
|
|
246
903
|
if (p !== AIWS_MANIFEST_FILE && ![...dirs].some((d) => p.startsWith(d)))
|
|
247
|
-
diagnostics.push({ file: p, severity: "error", message: "belongs to no control of the manifest" });
|
|
904
|
+
diagnostics.push({ file: p, severity: "error", message: "belongs to no control or template of the manifest" });
|
|
248
905
|
return { manifest, diagnostics };
|
|
249
906
|
}
|
|
250
907
|
async function exportBundle(client, workspaceId, entityIds, fetchAsset) {
|
|
@@ -297,38 +954,71 @@ async function writeHomeFile(nfsp, dir, name, bytes) {
|
|
|
297
954
|
}
|
|
298
955
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
299
956
|
async function collectSources(nfsp, dir) {
|
|
300
|
-
var _a, _b, _c;
|
|
301
957
|
const out = [];
|
|
302
958
|
const problems = [];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
959
|
+
const walk = async (folder, prefix, depth) => {
|
|
960
|
+
var _a, _b, _c;
|
|
961
|
+
let cursor;
|
|
962
|
+
do {
|
|
963
|
+
const page = await nfsp.list(typeof folder === "string" ? folder : { ref: folder }, { limit: 500, ...cursor ? { cursor } : {} }, ["base", "ident"]);
|
|
964
|
+
for (const e of page.entries) {
|
|
965
|
+
if (e.name.startsWith("."))
|
|
966
|
+
continue;
|
|
967
|
+
const name = `${prefix}${e.name}`;
|
|
968
|
+
if (e.target.kind === "dir") {
|
|
969
|
+
if (depth >= 8)
|
|
970
|
+
problems.push({ file: name, severity: "error", message: "sub-folders nest at most 8 levels" });
|
|
971
|
+
else
|
|
972
|
+
await walk(e.target.ref, `${name}/`, depth + 1);
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (e.target.kind !== "file") {
|
|
976
|
+
problems.push({ file: name, severity: "error", message: "only files and folders belong to a control source" });
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const ref = e.target.ref;
|
|
980
|
+
const objId = ref.type === "object" ? ref.obj_id : typeof ((_a = e.target.attrs) == null ? void 0 : _a.obj_id) === "string" ? e.target.attrs.obj_id : void 0;
|
|
981
|
+
out.push({
|
|
982
|
+
name,
|
|
983
|
+
...objId ? { obj_id: objId } : {},
|
|
984
|
+
...ref.type === "live" ? { node_id: ref.node_id } : {},
|
|
985
|
+
...typeof ((_b = e.target.attrs) == null ? void 0 : _b.etag) === "string" ? { etag: e.target.attrs.etag } : {},
|
|
986
|
+
...typeof ((_c = e.target.attrs) == null ? void 0 : _c.size) === "number" ? { size: e.target.attrs.size } : {}
|
|
987
|
+
});
|
|
312
988
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
...objId ? { obj_id: objId } : {},
|
|
318
|
-
...ref.type === "live" ? { node_id: ref.node_id } : {},
|
|
319
|
-
...typeof ((_b = e.target.attrs) == null ? void 0 : _b.etag) === "string" ? { etag: e.target.attrs.etag } : {},
|
|
320
|
-
...typeof ((_c = e.target.attrs) == null ? void 0 : _c.size) === "number" ? { size: e.target.attrs.size } : {}
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
cursor = page.truncated ? page.next_cursor : void 0;
|
|
324
|
-
} while (cursor);
|
|
989
|
+
cursor = page.truncated ? page.next_cursor : void 0;
|
|
990
|
+
} while (cursor);
|
|
991
|
+
};
|
|
992
|
+
await walk(dir, "", 0);
|
|
325
993
|
if (out.length === 0)
|
|
326
994
|
problems.push({ file: "", severity: "error", message: `${dir} holds no files` });
|
|
327
995
|
if (out.length > AIWS_MAX_SOURCE_FILES)
|
|
328
996
|
problems.push({ file: "", severity: "error", message: `a control holds at most ${AIWS_MAX_SOURCE_FILES} files` });
|
|
329
997
|
if (problems.some((p) => p.severity === "error"))
|
|
330
998
|
throw new AiwsSourceError(problems);
|
|
331
|
-
return out.sort((a, b) => a.name.
|
|
999
|
+
return out.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1000
|
+
}
|
|
1001
|
+
async function writeSourceFile(nfsp, dir, name, bytes) {
|
|
1002
|
+
const cut = name.lastIndexOf("/");
|
|
1003
|
+
await writeHomeFile(nfsp, cut < 0 ? dir : joinPath(dir, name.slice(0, cut)), cut < 0 ? name : name.slice(cut + 1), bytes);
|
|
1004
|
+
}
|
|
1005
|
+
async function sourcesDigest(nfsp, files) {
|
|
1006
|
+
const pairs = [];
|
|
1007
|
+
for (const f of files) {
|
|
1008
|
+
if (!f.node_id)
|
|
1009
|
+
throw new AiwsSourceError([{ file: f.name, severity: "error", message: "the file service did not name this file" }]);
|
|
1010
|
+
pairs.push([f.name, new Uint8Array(await (await nfsp.readFile(f.node_id)).arrayBuffer())]);
|
|
1011
|
+
}
|
|
1012
|
+
return aiwsContentDigest(pairs);
|
|
1013
|
+
}
|
|
1014
|
+
async function unbuiltChanges(client, nfsp, workspaceId, defEntityId, dir) {
|
|
1015
|
+
var _a;
|
|
1016
|
+
if (!await hasControlSource(nfsp, dir))
|
|
1017
|
+
return null;
|
|
1018
|
+
const home = await sourcesDigest(nfsp, await collectSources(nfsp, dir));
|
|
1019
|
+
const r = await client.extExport(workspaceId, [defEntityId], "pull");
|
|
1020
|
+
const built = String(((_a = r.controls[0]) == null ? void 0 : _a.digest) ?? "");
|
|
1021
|
+
return { changed: home !== built, home, built };
|
|
332
1022
|
}
|
|
333
1023
|
async function readBuildState(nfsp, dir) {
|
|
334
1024
|
try {
|
|
@@ -351,11 +1041,12 @@ async function buildControl(client, nfsp, workspaceId, dir, opts = {}) {
|
|
|
351
1041
|
const state = await readBuildState(nfsp, dir);
|
|
352
1042
|
const base = state && state.workspace_id === workspaceId ? { def_rev: state.def_rev, dir_rev: state.dir_rev } : void 0;
|
|
353
1043
|
const result = await client.extBuild(workspaceId, {
|
|
354
|
-
files: await buildFiles(nfsp, files),
|
|
1044
|
+
files: await buildFiles(nfsp, files, opts.upload),
|
|
355
1045
|
...opts.folderId ? { folder_id: opts.folderId } : {},
|
|
356
1046
|
...base ? { expect: base } : {},
|
|
357
1047
|
...opts.force ? { force: true } : {},
|
|
358
|
-
...opts.dryRun ? { dry_run: true } : {}
|
|
1048
|
+
...opts.dryRun ? { dry_run: true } : {},
|
|
1049
|
+
...opts.migration ? { migration: opts.migration } : {}
|
|
359
1050
|
});
|
|
360
1051
|
if (!opts.dryRun && typeof result.def_entity_id === "string" && typeof result.def_rev === "number") {
|
|
361
1052
|
await writeBuildState(nfsp, dir, { workspace_id: workspaceId, def_entity_id: result.def_entity_id, def_rev: result.def_rev, dir_rev: Number(result.dir_rev ?? 0), ident: String(result.ident ?? ""), at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -365,9 +1056,9 @@ async function buildControl(client, nfsp, workspaceId, dir, opts = {}) {
|
|
|
365
1056
|
async function pullControl(client, nfsp, workspaceId, defEntityId, outDir, fetchAsset) {
|
|
366
1057
|
const r = await client.extExport(workspaceId, [defEntityId], "pull");
|
|
367
1058
|
const control = r.controls[0];
|
|
368
|
-
|
|
1059
|
+
await ensureFolder(nfsp, outDir);
|
|
369
1060
|
for (const f of control.files)
|
|
370
|
-
await
|
|
1061
|
+
await writeSourceFile(nfsp, outDir, f.name, f.text !== void 0 ? encoder.encode(f.text) : await fetchAsset(workspaceId, String(f.obj_id)));
|
|
371
1062
|
const state = { workspace_id: workspaceId, def_entity_id: control.entity_id, def_rev: control.def_rev, dir_rev: control.dir_rev, ident: control.ident, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
372
1063
|
await writeBuildState(nfsp, outDir, state);
|
|
373
1064
|
return { files: control.files.map((f) => f.name), state };
|
|
@@ -416,6 +1107,43 @@ async function watchControl(nfsp, dir, onChange, opts = {}) {
|
|
|
416
1107
|
}
|
|
417
1108
|
function controlSkeleton(opts) {
|
|
418
1109
|
const title = opts.title ?? opts.name;
|
|
1110
|
+
if (opts.kind === "composite") {
|
|
1111
|
+
const fixed = opts.algorithm === "fixed";
|
|
1112
|
+
const slots = opts.slots ?? { items: { placement: fixed ? "free" : "stack", title: "Items" } };
|
|
1113
|
+
const composition = {
|
|
1114
|
+
layout: fixed ? { algorithm: "fixed", padding: 12 } : { algorithm: "stack", direction: "column", spacing: "{{config.gap}}", padding: 12 },
|
|
1115
|
+
slots,
|
|
1116
|
+
chrome: { background: "{{config.background}}", radius: 12, title: { text: "{{config.title}}" }, empty: "Drop Blocks here" }
|
|
1117
|
+
};
|
|
1118
|
+
const component = {
|
|
1119
|
+
manifest_version: AIWS_MANIFEST_VERSION,
|
|
1120
|
+
ident: { author_did: opts.authorDid, name: opts.name },
|
|
1121
|
+
kind: "composite",
|
|
1122
|
+
title,
|
|
1123
|
+
description: `${title}: a panel of Blocks`,
|
|
1124
|
+
default_size: { w: 320, h: 360 },
|
|
1125
|
+
config_schema: { type: "object", properties: {
|
|
1126
|
+
title: { type: "string", title: "Title", default: title },
|
|
1127
|
+
background: { type: "string", title: "Background", default: "#f6f7fb" },
|
|
1128
|
+
gap: { type: "integer", title: "Gap (px)", default: 8, minimum: 0, maximum: 64 }
|
|
1129
|
+
} },
|
|
1130
|
+
entry: AIWS_COMPOSITION_FILE,
|
|
1131
|
+
deps: opts.deps ?? [],
|
|
1132
|
+
catalog: { group: "custom", description: `${title}` },
|
|
1133
|
+
agent: "AGENT.md"
|
|
1134
|
+
};
|
|
1135
|
+
const names = Object.keys(slots).map((n) => `\`${n}\``).join(", ");
|
|
1136
|
+
return {
|
|
1137
|
+
[AIWS_COMPONENT_FILE]: `${JSON.stringify(component, null, 2)}
|
|
1138
|
+
`,
|
|
1139
|
+
[AIWS_COMPOSITION_FILE]: `${JSON.stringify(composition, null, 2)}
|
|
1140
|
+
`,
|
|
1141
|
+
"AGENT.md": `# ${title}
|
|
1142
|
+
|
|
1143
|
+
A composite: an instance is a \`buckyos.container { kind: "composite", def_ref }\`; put Blocks under it with \`slot\` set to one of ${names}. Config: \`title\`, \`background\`, \`gap\`.
|
|
1144
|
+
`
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
419
1147
|
const base = {
|
|
420
1148
|
manifest_version: AIWS_MANIFEST_VERSION,
|
|
421
1149
|
ident: { author_did: opts.authorDid, name: opts.name },
|
|
@@ -435,13 +1163,22 @@ function controlSkeleton(opts) {
|
|
|
435
1163
|
Bind \`data\` to a table. Config: \`label\` (text).
|
|
436
1164
|
`;
|
|
437
1165
|
if (opts.kind === "html") {
|
|
1166
|
+
const events = { clicked: { title: "Clicked", description: "Raised when someone clicks the button (in run mode)", params: { count: { type: "number", description: "Rows of the bound table" } } } };
|
|
438
1167
|
return {
|
|
439
|
-
[AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, host_api: AIWS_HTML_API_VERSION, entry: { html: "view.html", css: "style.css", js: "behavior.js" } }, null, 2)}
|
|
1168
|
+
[AIWS_COMPONENT_FILE]: `${JSON.stringify({ ...base, host_api: AIWS_HTML_API_VERSION, events, entry: { html: "view.html", css: "style.css", js: "behavior.js" } }, null, 2)}
|
|
440
1169
|
`,
|
|
441
|
-
"view.html": '<div class="card"><div class="label" id="label"></div><div class="value" id="value">—</div></div>\n',
|
|
1170
|
+
"view.html": '<div class="card"><div class="label" id="label"></div><div class="value" id="value">—</div><button id="go" type="button">Go</button></div>\n',
|
|
442
1171
|
"style.css": ".card{font:14px system-ui;padding:12px}.label{opacity:.7}.value{font-size:28px;font-weight:600}\n",
|
|
443
|
-
"behavior.js":
|
|
444
|
-
|
|
1172
|
+
"behavior.js": [
|
|
1173
|
+
"document.getElementById('label').textContent = (aiws.context.config || {}).label || ''",
|
|
1174
|
+
"var count = 0",
|
|
1175
|
+
"if (aiws.bindings.data) aiws.input('data').rows().then(function (rows) { count = rows.length; document.getElementById('value').textContent = String(count) })",
|
|
1176
|
+
"// the public event of component.json: an instance binds handlers to it in its `on` (host API 3)",
|
|
1177
|
+
"document.getElementById('go').addEventListener('click', function () { aiws.emit('clicked', { count: count }) })",
|
|
1178
|
+
""
|
|
1179
|
+
].join("\n"),
|
|
1180
|
+
"AGENT.md": `${agent}Event: \`clicked\` (\`count\`), raised by its button; bind handlers to it in the instance's \`on\`.
|
|
1181
|
+
`
|
|
445
1182
|
};
|
|
446
1183
|
}
|
|
447
1184
|
return {
|
|
@@ -464,17 +1201,22 @@ async function createControlSource(nfsp, dir, files) {
|
|
|
464
1201
|
export {
|
|
465
1202
|
AIWS_BUILD_STATE_FILE,
|
|
466
1203
|
AIWS_COMPONENT_FILE,
|
|
1204
|
+
AIWS_COMPOSITION_FILE,
|
|
467
1205
|
AIWS_DECLARATIVE_API_VERSION,
|
|
468
1206
|
AIWS_HTML_API_VERSION,
|
|
469
1207
|
AIWS_MANIFEST_FILE,
|
|
470
1208
|
AIWS_MANIFEST_VERSION,
|
|
471
1209
|
AIWS_MAX_INLINE_BYTES,
|
|
472
1210
|
AIWS_MAX_SOURCE_FILES,
|
|
1211
|
+
AIWS_TEMPLATE_FORMAT,
|
|
473
1212
|
AiwsSourceError,
|
|
474
1213
|
aiwsContentDigest,
|
|
1214
|
+
aiwsTemplateDigest,
|
|
475
1215
|
buildControl,
|
|
476
1216
|
buildFiles,
|
|
477
1217
|
checkComponent,
|
|
1218
|
+
checkComposition,
|
|
1219
|
+
checkDeclarative,
|
|
478
1220
|
collectSources,
|
|
479
1221
|
compatManifest,
|
|
480
1222
|
controlSkeleton,
|
|
@@ -486,14 +1228,21 @@ export {
|
|
|
486
1228
|
hasControlSource,
|
|
487
1229
|
homeControlDir,
|
|
488
1230
|
homeExportDir,
|
|
1231
|
+
isSourcePath,
|
|
489
1232
|
joinPath,
|
|
1233
|
+
packBuildFiles,
|
|
490
1234
|
pullControl,
|
|
491
1235
|
readBuildState,
|
|
1236
|
+
slotCells,
|
|
492
1237
|
sourceStateKey,
|
|
1238
|
+
sourcesDigest,
|
|
493
1239
|
sourcesKey,
|
|
1240
|
+
templateDir,
|
|
1241
|
+
unbuiltChanges,
|
|
494
1242
|
verifyBundle,
|
|
495
1243
|
watchControl,
|
|
496
1244
|
writeBuildState,
|
|
497
|
-
writeHomeFile
|
|
1245
|
+
writeHomeFile,
|
|
1246
|
+
writeSourceFile
|
|
498
1247
|
};
|
|
499
1248
|
//# sourceMappingURL=aiws_ext.mjs.map
|