@pwfabric/cli 0.0.0-stage → 1.0.0-rc.1
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 +122 -2
- package/dist/args.d.ts +11 -0
- package/dist/args.js +43 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +10 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.js +56 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/build.d.ts +3 -0
- package/dist/commands/build.js +55 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +119 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +12 -0
- package/dist/commands/dev.js +107 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/lib/flags.d.ts +10 -0
- package/dist/commands/lib/flags.js +33 -0
- package/dist/commands/lib/flags.js.map +1 -0
- package/dist/commands/lib/identity.d.ts +38 -0
- package/dist/commands/lib/identity.js +120 -0
- package/dist/commands/lib/identity.js.map +1 -0
- package/dist/commands/lib/project.d.ts +34 -0
- package/dist/commands/lib/project.js +78 -0
- package/dist/commands/lib/project.js.map +1 -0
- package/dist/commands/lib/report.d.ts +15 -0
- package/dist/commands/lib/report.js +75 -0
- package/dist/commands/lib/report.js.map +1 -0
- package/dist/commands/lib/starter.d.ts +41 -0
- package/dist/commands/lib/starter.js +136 -0
- package/dist/commands/lib/starter.js.map +1 -0
- package/dist/commands/lib/static-server.d.ts +45 -0
- package/dist/commands/lib/static-server.js +176 -0
- package/dist/commands/lib/static-server.js.map +1 -0
- package/dist/commands/pack.d.ts +5 -0
- package/dist/commands/pack.js +56 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/registry.d.ts +7 -0
- package/dist/commands/registry.js +16 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/run.d.ts +14 -0
- package/dist/commands/run.js +79 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/types.d.ts +34 -0
- package/dist/commands/types.js +10 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.js +33 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/dev/assemble.d.ts +47 -0
- package/dist/dev/assemble.js +339 -0
- package/dist/dev/assemble.js.map +1 -0
- package/dist/dev/compile.d.ts +54 -0
- package/dist/dev/compile.js +215 -0
- package/dist/dev/compile.js.map +1 -0
- package/dist/dev/csp.d.ts +67 -0
- package/dist/dev/csp.js +260 -0
- package/dist/dev/csp.js.map +1 -0
- package/dist/dev/diagnostics.d.ts +21 -0
- package/dist/dev/diagnostics.js +22 -0
- package/dist/dev/diagnostics.js.map +1 -0
- package/dist/dev/envelope.d.ts +92 -0
- package/dist/dev/envelope.js +154 -0
- package/dist/dev/envelope.js.map +1 -0
- package/dist/dev/media.d.ts +53 -0
- package/dist/dev/media.js +128 -0
- package/dist/dev/media.js.map +1 -0
- package/dist/dev/needs-host.d.ts +2 -0
- package/dist/dev/needs-host.js +32 -0
- package/dist/dev/needs-host.js.map +1 -0
- package/dist/dev/output.d.ts +9 -0
- package/dist/dev/output.js +32 -0
- package/dist/dev/output.js.map +1 -0
- package/dist/dev/overlay.d.ts +21 -0
- package/dist/dev/overlay.js +59 -0
- package/dist/dev/overlay.js.map +1 -0
- package/dist/dev/server.d.ts +27 -0
- package/dist/dev/server.js +268 -0
- package/dist/dev/server.js.map +1 -0
- package/dist/dev/shell.d.ts +94 -0
- package/dist/dev/shell.js +267 -0
- package/dist/dev/shell.js.map +1 -0
- package/dist/dev/sources.d.ts +45 -0
- package/dist/dev/sources.js +165 -0
- package/dist/dev/sources.js.map +1 -0
- package/dist/dev/theme.d.ts +51 -0
- package/dist/dev/theme.js +185 -0
- package/dist/dev/theme.js.map +1 -0
- package/dist/dev/types.d.ts +40 -0
- package/dist/dev/types.js +11 -0
- package/dist/dev/types.js.map +1 -0
- package/dist/dev/watcher.d.ts +29 -0
- package/dist/dev/watcher.js +150 -0
- package/dist/dev/watcher.js.map +1 -0
- package/dist/host/definitions.d.ts +61 -0
- package/dist/host/definitions.js +136 -0
- package/dist/host/definitions.js.map +1 -0
- package/dist/host/http.d.ts +39 -0
- package/dist/host/http.js +156 -0
- package/dist/host/http.js.map +1 -0
- package/dist/host/local-host.d.ts +59 -0
- package/dist/host/local-host.js +420 -0
- package/dist/host/local-host.js.map +1 -0
- package/dist/host/oidc.d.ts +46 -0
- package/dist/host/oidc.js +192 -0
- package/dist/host/oidc.js.map +1 -0
- package/dist/host/query.d.ts +35 -0
- package/dist/host/query.js +158 -0
- package/dist/host/query.js.map +1 -0
- package/dist/host/receipts.d.ts +25 -0
- package/dist/host/receipts.js +72 -0
- package/dist/host/receipts.js.map +1 -0
- package/dist/host/records.d.ts +73 -0
- package/dist/host/records.js +229 -0
- package/dist/host/records.js.map +1 -0
- package/dist/host/tokens.d.ts +28 -0
- package/dist/host/tokens.js +52 -0
- package/dist/host/tokens.js.map +1 -0
- package/dist/host/types.d.ts +32 -0
- package/dist/host/types.js +11 -0
- package/dist/host/types.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -3
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* assemblePwapp — a pwpack app folder in, a static pwapp out.
|
|
3
|
+
*
|
|
4
|
+
* Reads the app and the pack builds it pins (from the project's cache),
|
|
5
|
+
* compiles every block, capability and engine atom with the engine's
|
|
6
|
+
* compiler, resolves the app's theme, builds the envelope the runner reads,
|
|
7
|
+
* and lays out the served folder of the pwapp format: shell, runner,
|
|
8
|
+
* renderer, shims, block and engine modules, envelope and `publish.json`.
|
|
9
|
+
*
|
|
10
|
+
* Pure in its output: the same folder, cache and runtime bundles give the
|
|
11
|
+
* same files (only `publish.json`'s `publishedAt` carries a clock, and it can
|
|
12
|
+
* be pinned). Nothing is fetched; every input is on disk.
|
|
13
|
+
*/
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { hashPack } from '@pwfabric/pack';
|
|
16
|
+
import { atomKindMeta } from '@pwfabric/contracts';
|
|
17
|
+
import { compileSources, createCompileCache } from './compile.js';
|
|
18
|
+
import { hasErrors, sortDiagnostics } from './diagnostics.js';
|
|
19
|
+
import { blockTypesOf, buildConnections, buildProjectJson, deriveNavigation, mergeEnvelopeEntries, moduleFileName, publisherOf, } from './envelope.js';
|
|
20
|
+
import { collectMedia, fillMediaSlots } from './media.js';
|
|
21
|
+
import { deriveCsp, headersFile } from './csp.js';
|
|
22
|
+
import { bootScript, buildShellHtml, importMapText, loadRuntimeAssets, runnerLocaleOf, seedScript, shimsFor, } from './shell.js';
|
|
23
|
+
import { isRecord, jsonOf, loadSources, PWF_DIR } from './sources.js';
|
|
24
|
+
import { appearanceConfigOf, buildThemeCss, resolveAppearance, themeAtomOf, themeWebfonts, webfontStylesheetHref, } from './theme.js';
|
|
25
|
+
const enc = new TextEncoder();
|
|
26
|
+
const json = (value) => enc.encode(JSON.stringify(value, null, 2));
|
|
27
|
+
const hexOf = (hash) => hash.replace(/^sha256:/, '');
|
|
28
|
+
/** Every name a block atom answers to in a receipt: its id, its manifest name, and its short forms. */
|
|
29
|
+
export function blockNames(atom) {
|
|
30
|
+
const names = new Set([atom.id]);
|
|
31
|
+
const name = atom.manifest?.['name'];
|
|
32
|
+
if (typeof name === 'string' && name)
|
|
33
|
+
names.add(name);
|
|
34
|
+
const dot = atom.id.indexOf('.');
|
|
35
|
+
if (dot >= 0)
|
|
36
|
+
names.add(atom.id.slice(dot + 1));
|
|
37
|
+
names.add(atom.id.slice(atom.id.lastIndexOf('.') + 1));
|
|
38
|
+
return [...names];
|
|
39
|
+
}
|
|
40
|
+
function titleOf(plan, id) {
|
|
41
|
+
if (isRecord(plan) && typeof plan['title'] === 'string' && plan['title'].trim())
|
|
42
|
+
return plan['title'].trim();
|
|
43
|
+
const last = id.split('/').pop() ?? id;
|
|
44
|
+
return last
|
|
45
|
+
.split(/[-_]+/)
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.map((w) => w[0].toUpperCase() + w.slice(1))
|
|
48
|
+
.join(' ');
|
|
49
|
+
}
|
|
50
|
+
function atomLabel(atom) {
|
|
51
|
+
return atom.source.origin === 'app' ? atom.id : `${atom.id} (${atom.source.id}@${atom.source.version})`;
|
|
52
|
+
}
|
|
53
|
+
const EMPTY_STATS = { pages: 0, blocks: 0, engines: 0, atoms: 0, compiled: 0, fromCache: 0, failed: 0 };
|
|
54
|
+
/** Assembles the pwapp for the app folder at `root`. */
|
|
55
|
+
export async function assemblePwapp(root, options = {}) {
|
|
56
|
+
const started = performance.now();
|
|
57
|
+
const diagnostics = [];
|
|
58
|
+
const files = new Map();
|
|
59
|
+
const done = (stats) => {
|
|
60
|
+
const sorted = sortDiagnostics(diagnostics);
|
|
61
|
+
return { ok: !hasErrors(sorted), files, diagnostics: sorted, stats, ms: Math.round(performance.now() - started) };
|
|
62
|
+
};
|
|
63
|
+
const sources = await loadSources(root, options.packMemo);
|
|
64
|
+
diagnostics.push(...sources.diagnostics);
|
|
65
|
+
const { app, identity } = sources;
|
|
66
|
+
if (!app || !identity)
|
|
67
|
+
return done(EMPTY_STATS);
|
|
68
|
+
if (!identity.app) {
|
|
69
|
+
diagnostics.push({
|
|
70
|
+
severity: 'error',
|
|
71
|
+
code: 'NOT_AN_APP',
|
|
72
|
+
message: 'pwpack.json declares no "app" member, so this folder is a pack, not an app — there is nothing to run',
|
|
73
|
+
path: 'pwpack.json',
|
|
74
|
+
});
|
|
75
|
+
return done(EMPTY_STATS);
|
|
76
|
+
}
|
|
77
|
+
// ── pages ────────────────────────────────────────────────────────
|
|
78
|
+
const reported = new Set(diagnostics.map((d) => d.path));
|
|
79
|
+
const pages = [];
|
|
80
|
+
for (const key of identity.app.surfaceOrder) {
|
|
81
|
+
const path = `app/surfaces/${key}.receipt.json`;
|
|
82
|
+
const bytes = app.folder.files.get(path);
|
|
83
|
+
const value = jsonOf(app.folder, path);
|
|
84
|
+
if (!bytes) {
|
|
85
|
+
if (!reported.has(path)) {
|
|
86
|
+
diagnostics.push({ severity: 'error', code: 'RECEIPT_MISSING', message: `page "${key}" is in app.surfaceOrder but ${path} does not exist`, path });
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (value instanceof Error || !isRecord(value) || !Array.isArray(value['blocks'])) {
|
|
91
|
+
diagnostics.push({
|
|
92
|
+
severity: 'error',
|
|
93
|
+
code: 'RECEIPT_INVALID',
|
|
94
|
+
message: value instanceof Error ? `not JSON: ${value.message}` : 'a receipt is an object with a "blocks" array',
|
|
95
|
+
path,
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
pages.push({ key, path, bytes, receipt: value });
|
|
100
|
+
}
|
|
101
|
+
const entry = identity.app.entry;
|
|
102
|
+
if (!identity.app.surfaceOrder.includes(entry)) {
|
|
103
|
+
diagnostics.push({ severity: 'error', code: 'ENTRY_NOT_A_PAGE', message: `app.entry "${entry}" is not in app.surfaceOrder`, path: 'pwpack.json' });
|
|
104
|
+
}
|
|
105
|
+
// ── definitions ──────────────────────────────────────────────────
|
|
106
|
+
const definition = (name) => {
|
|
107
|
+
const path = `app/definitions/${name}.json`;
|
|
108
|
+
const value = jsonOf(app.folder, path);
|
|
109
|
+
if (value instanceof Error) {
|
|
110
|
+
diagnostics.push({ severity: 'error', code: 'DEFINITION_INVALID', message: `not JSON: ${value.message}`, path });
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
};
|
|
115
|
+
const navigationDoc = definition('navigation');
|
|
116
|
+
const appearanceDoc = definition('appearance');
|
|
117
|
+
const connectionsDoc = definition('connections');
|
|
118
|
+
const authDoc = definition('auth');
|
|
119
|
+
const plan = jsonOf(app.folder, 'app/plan.json');
|
|
120
|
+
// ── compile ──────────────────────────────────────────────────────
|
|
121
|
+
const all = [app, ...sources.packs];
|
|
122
|
+
const cache = options.cache ?? createCompileCache(options.cacheDir === undefined ? join(root, PWF_DIR, 'build-cache') : options.cacheDir);
|
|
123
|
+
const atoms = await compileSources(all, cache);
|
|
124
|
+
// ── themes ───────────────────────────────────────────────────────
|
|
125
|
+
const themes = [];
|
|
126
|
+
for (const source of all) {
|
|
127
|
+
for (const ref of source.validation.atoms) {
|
|
128
|
+
if (ref.kind !== 'ui-appearance')
|
|
129
|
+
continue;
|
|
130
|
+
const theme = themeAtomOf(ref.id, jsonOf(source.folder, ref.relPath), source.label || source.id);
|
|
131
|
+
if (theme)
|
|
132
|
+
themes.push(theme);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const appearance = resolveAppearance(appearanceDoc, themes, diagnostics);
|
|
136
|
+
// ── blocks ───────────────────────────────────────────────────────
|
|
137
|
+
const byName = new Map();
|
|
138
|
+
for (const atom of atoms) {
|
|
139
|
+
if (atom.kind !== 'block')
|
|
140
|
+
continue;
|
|
141
|
+
for (const name of blockNames(atom)) {
|
|
142
|
+
const list = byName.get(name) ?? [];
|
|
143
|
+
if (!list.includes(atom))
|
|
144
|
+
list.push(atom);
|
|
145
|
+
byName.set(name, list);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const used = new Set();
|
|
149
|
+
const blockManifest = [];
|
|
150
|
+
const modules = new Map();
|
|
151
|
+
const firstUse = new Map();
|
|
152
|
+
for (const page of pages)
|
|
153
|
+
for (const t of blockTypesOf(page.receipt))
|
|
154
|
+
if (!firstUse.has(t))
|
|
155
|
+
firstUse.set(t, page);
|
|
156
|
+
for (const [type, page] of [...firstUse].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))) {
|
|
157
|
+
const candidates = byName.get(type) ?? [];
|
|
158
|
+
const atom = candidates[0];
|
|
159
|
+
if (!atom) {
|
|
160
|
+
diagnostics.push({
|
|
161
|
+
severity: 'error',
|
|
162
|
+
code: 'UNKNOWN_BLOCK_TYPE',
|
|
163
|
+
message: `page "${page.key}" places a block of type "${type}", which neither the app nor its pinned packs define`,
|
|
164
|
+
path: page.path,
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
used.add(atom);
|
|
169
|
+
if (candidates.length > 1) {
|
|
170
|
+
diagnostics.push({
|
|
171
|
+
severity: 'info',
|
|
172
|
+
code: 'BLOCK_TYPE_SHADOWED',
|
|
173
|
+
message: `block type "${type}" resolves to ${atomLabel(atom)}; also defined by ${candidates.slice(1).map(atomLabel).join(', ')}`,
|
|
174
|
+
path: page.path,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (!atom.code || !atom.hash) {
|
|
178
|
+
diagnostics.push({
|
|
179
|
+
severity: 'error',
|
|
180
|
+
code: 'BLOCK_NOT_COMPILED',
|
|
181
|
+
message: `block type "${type}" (${atomLabel(atom)}) did not compile (see the compile errors reported for ${atom.relPath})`,
|
|
182
|
+
path: page.path,
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const file = `blocks/${moduleFileName(type, hexOf(atom.hash))}`;
|
|
187
|
+
modules.set(file, enc.encode(atom.code));
|
|
188
|
+
blockManifest.push({
|
|
189
|
+
blockType: type,
|
|
190
|
+
moduleUrl: file,
|
|
191
|
+
integrity: hexOf(atom.hash),
|
|
192
|
+
publisher: publisherOf(atom.source.id),
|
|
193
|
+
publisherVerified: false,
|
|
194
|
+
definition: { name: type },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// ── engines ──────────────────────────────────────────────────────
|
|
198
|
+
const connections = buildConnections(connectionsDoc, diagnostics);
|
|
199
|
+
const engineManifest = [];
|
|
200
|
+
for (const conn of connections?.connections ?? []) {
|
|
201
|
+
const name = conn['name'];
|
|
202
|
+
const wanted = typeof conn['connector'] === 'string' ? conn['connector'] : name;
|
|
203
|
+
const atom = atoms.find((a) => (a.kind === 'connector' || a.kind === 'provider') && a.manifest?.['connection'] === wanted);
|
|
204
|
+
if (!atom)
|
|
205
|
+
continue;
|
|
206
|
+
used.add(atom);
|
|
207
|
+
const kind = atom.kind;
|
|
208
|
+
if (atom.code && atom.hash) {
|
|
209
|
+
const file = `engines/${moduleFileName(atom.id, hexOf(atom.hash))}`;
|
|
210
|
+
modules.set(file, enc.encode(atom.code));
|
|
211
|
+
engineManifest.push({ connection: name, atomId: atom.id, kind, moduleUrl: file, integrity: hexOf(atom.hash) });
|
|
212
|
+
}
|
|
213
|
+
else if (atom.manifest?.['engine'] !== undefined) {
|
|
214
|
+
engineManifest.push({ connection: name, atomId: atom.id, kind, engine: atom.manifest['engine'] });
|
|
215
|
+
}
|
|
216
|
+
else if (atom.status === 'failed') {
|
|
217
|
+
diagnostics.push({
|
|
218
|
+
severity: 'warning',
|
|
219
|
+
code: 'ENGINE_NOT_COMPILED',
|
|
220
|
+
message: `connection "${name}": engine ${atomLabel(atom)} did not compile, so its panels fall back to sample data`,
|
|
221
|
+
path: 'app/definitions/connections.json',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// ── atom diagnostics ─────────────────────────────────────────────
|
|
226
|
+
// The app's own atoms and the atoms it uses speak at full volume; an unused
|
|
227
|
+
// atom of a pinned pack cannot break this app, so its errors are warnings.
|
|
228
|
+
for (const atom of atoms) {
|
|
229
|
+
const loud = atom.source.origin === 'app' || used.has(atom);
|
|
230
|
+
for (const d of atom.diagnostics) {
|
|
231
|
+
const engineDegrades = atomKindMeta(atom.kind).onFailure === 'degrade';
|
|
232
|
+
diagnostics.push(loud && !engineDegrades ? d : d.severity === 'error' ? { ...d, severity: 'warning' } : d);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// ── the served folder ────────────────────────────────────────────
|
|
236
|
+
const runtime = options.runtime ?? (await loadRuntimeAssets());
|
|
237
|
+
const config = appearanceConfigOf(appearance);
|
|
238
|
+
const title = titleOf(plan, identity.id);
|
|
239
|
+
const locale = isRecord(plan) && typeof plan['locale'] === 'string' ? plan['locale'] : undefined;
|
|
240
|
+
const shell = (csp) => enc.encode(buildShellHtml({
|
|
241
|
+
title,
|
|
242
|
+
...(locale !== undefined ? { lang: locale } : {}),
|
|
243
|
+
...(config ? { appearance: config } : {}),
|
|
244
|
+
...(options.shellExtra !== undefined ? { bodyExtra: options.shellExtra } : {}),
|
|
245
|
+
...(csp !== undefined ? { csp } : {}),
|
|
246
|
+
}));
|
|
247
|
+
// Written again with its policy once everything the policy reads is known.
|
|
248
|
+
files.set('index.html', shell());
|
|
249
|
+
files.set('assets/seed.js', enc.encode(seedScript()));
|
|
250
|
+
files.set('assets/boot.js', enc.encode(bootScript(runnerLocaleOf(locale))));
|
|
251
|
+
files.set('assets/runner.js', enc.encode(runtime.runnerJs));
|
|
252
|
+
if (runtime.runnerMap !== undefined)
|
|
253
|
+
files.set('assets/runner.js.map', enc.encode(runtime.runnerMap));
|
|
254
|
+
files.set('assets/embed.js', enc.encode(runtime.embedJs));
|
|
255
|
+
files.set('assets/theme.css', enc.encode(buildThemeCss(config)));
|
|
256
|
+
for (const [name, source] of await shimsFor(runtime.embedJs))
|
|
257
|
+
files.set(`shims/${name}`, enc.encode(source));
|
|
258
|
+
for (const [file, bytes] of [...modules].sort((a, b) => (a[0] < b[0] ? -1 : 1)))
|
|
259
|
+
files.set(file, bytes);
|
|
260
|
+
files.set('blocks/manifest.json', json(blockManifest));
|
|
261
|
+
files.set('engines/manifest.json', json(engineManifest));
|
|
262
|
+
files.set('pwpack/project.json', json(buildProjectJson({ id: identity.id, title, entry, pages, auth: authDoc })));
|
|
263
|
+
for (const page of pages)
|
|
264
|
+
files.set(`pwpack/surfaces/${page.key}.receipt.json`, page.bytes);
|
|
265
|
+
files.set('pwpack/navigation.json', json(isRecord(navigationDoc) ? navigationDoc : deriveNavigation(pages, entry)));
|
|
266
|
+
if (appearance)
|
|
267
|
+
files.set('pwpack/appearance.json', json(appearance));
|
|
268
|
+
// Always written: the runner asks for it on every boot, and an app that
|
|
269
|
+
// declares no connection should not cost the visitor a 404 in the console.
|
|
270
|
+
files.set('pwpack/connections.json', json(connections ?? { connections: [] }));
|
|
271
|
+
const media = collectMedia(app.folder, diagnostics);
|
|
272
|
+
for (const [file, bytes] of media.files)
|
|
273
|
+
files.set(`pwpack/${file}`, bytes);
|
|
274
|
+
mergeEnvelopeEntries(files, options.envelopeEntries, diagnostics);
|
|
275
|
+
// After the host's entries, so a receipt the host rewrote gets its media too.
|
|
276
|
+
for (const page of pages) {
|
|
277
|
+
const path = `pwpack/surfaces/${page.key}.receipt.json`;
|
|
278
|
+
const filled = fillMediaSlots(files.get(path), media.slots.get(page.key));
|
|
279
|
+
if (filled)
|
|
280
|
+
files.set(path, filled);
|
|
281
|
+
}
|
|
282
|
+
// ── the Content Security Policy ──────────────────────────────────
|
|
283
|
+
const servedReceipts = new Map();
|
|
284
|
+
for (const page of pages) {
|
|
285
|
+
const bytes = files.get(`pwpack/surfaces/${page.key}.receipt.json`);
|
|
286
|
+
try {
|
|
287
|
+
servedReceipts.set(page.key, bytes ? JSON.parse(new TextDecoder().decode(bytes)) : page.receipt);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
servedReceipts.set(page.key, page.receipt);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const envelopeAuthBytes = files.get('pwpack/auth.json');
|
|
294
|
+
const csp = deriveCsp({
|
|
295
|
+
importMapText: importMapText(),
|
|
296
|
+
receipts: servedReceipts,
|
|
297
|
+
connections,
|
|
298
|
+
engines: engineManifest,
|
|
299
|
+
envelopeAuth: envelopeAuthBytes ? JSON.parse(new TextDecoder().decode(envelopeAuthBytes)) : undefined,
|
|
300
|
+
authDefinition: authDoc,
|
|
301
|
+
media: media.records,
|
|
302
|
+
webfontHref: webfontStylesheetHref(themeWebfonts(config)),
|
|
303
|
+
});
|
|
304
|
+
files.set('index.html', shell(csp.meta));
|
|
305
|
+
files.set('_headers', enc.encode(headersFile(csp.header)));
|
|
306
|
+
const publishedAtoms = atoms
|
|
307
|
+
.filter((a) => used.has(a))
|
|
308
|
+
.map((a) => {
|
|
309
|
+
const module = blockManifest.find((b) => b.integrity === (a.hash ? hexOf(a.hash) : ''))?.moduleUrl ??
|
|
310
|
+
engineManifest.find((e) => e.atomId === a.id && e.moduleUrl)?.moduleUrl;
|
|
311
|
+
return {
|
|
312
|
+
kind: a.kind,
|
|
313
|
+
id: a.id,
|
|
314
|
+
hash: a.sourceHash,
|
|
315
|
+
...(module && a.hash ? { module, integrity: hexOf(a.hash) } : {}),
|
|
316
|
+
};
|
|
317
|
+
});
|
|
318
|
+
files.set('publish.json', json({
|
|
319
|
+
formatVersion: '1.0',
|
|
320
|
+
app: { id: identity.id, version: identity.version },
|
|
321
|
+
liveVersion: identity.version,
|
|
322
|
+
publishedAt: options.publishedAt ?? new Date().toISOString(),
|
|
323
|
+
packs: sources.packs.map((p) => ({ id: p.id, version: p.version, hash: p.pinnedHash ?? hashPack(p.folder) })),
|
|
324
|
+
atoms: publishedAtoms,
|
|
325
|
+
...(media.records.length > 0 ? { media: media.records } : {}),
|
|
326
|
+
csp: { policy: csp.header, ...(csp.unresolved.length > 0 ? { unresolved: csp.unresolved } : {}) },
|
|
327
|
+
}));
|
|
328
|
+
const compiled = atoms.filter((a) => a.status === 'compiled' || a.status === 'precompiled');
|
|
329
|
+
return done({
|
|
330
|
+
pages: pages.length,
|
|
331
|
+
blocks: blockManifest.length,
|
|
332
|
+
engines: engineManifest.length,
|
|
333
|
+
atoms: atoms.length,
|
|
334
|
+
compiled: compiled.length,
|
|
335
|
+
fromCache: compiled.filter((a) => a.fromCache).length,
|
|
336
|
+
failed: atoms.filter((a) => a.status === 'failed').length,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=assemble.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/dev/assemble.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * assemblePwapp — a pwpack app folder in, a static pwapp out.\n *\n * Reads the app and the pack builds it pins (from the project's cache),\n * compiles every block, capability and engine atom with the engine's\n * compiler, resolves the app's theme, builds the envelope the runner reads,\n * and lays out the served folder of the pwapp format: shell, runner,\n * renderer, shims, block and engine modules, envelope and `publish.json`.\n *\n * Pure in its output: the same folder, cache and runtime bundles give the\n * same files (only `publish.json`'s `publishedAt` carries a clock, and it can\n * be pinned). Nothing is fetched; every input is on disk.\n */\nimport { join } from 'node:path'\nimport { hashPack } from '@pwfabric/pack'\nimport { atomKindMeta } from '@pwfabric/contracts'\nimport { compileSources, createCompileCache, type CompileCache, type CompiledAtom } from './compile.js'\nimport { hasErrors, sortDiagnostics, type AssembleDiagnostic } from './diagnostics.js'\nimport {\n blockTypesOf,\n buildConnections,\n buildProjectJson,\n deriveNavigation,\n mergeEnvelopeEntries,\n moduleFileName,\n publisherOf,\n type BlockManifestEntry,\n type EngineManifestEntry,\n type Page,\n} from './envelope.js'\nimport { collectMedia, fillMediaSlots } from './media.js'\nimport { deriveCsp, headersFile } from './csp.js'\nimport {\n bootScript,\n buildShellHtml,\n importMapText,\n loadRuntimeAssets,\n runnerLocaleOf,\n seedScript,\n shimsFor,\n type RuntimeAssets,\n} from './shell.js'\nimport { isRecord, jsonOf, loadSources, PWF_DIR, type PackMemo, type SourcePack } from './sources.js'\nimport {\n appearanceConfigOf,\n buildThemeCss,\n resolveAppearance,\n themeAtomOf,\n themeWebfonts,\n webfontStylesheetHref,\n type ThemeAtom,\n} from './theme.js'\n\nexport interface AssembleOptions {\n /** Markup appended to the shell's `<body>` — the dev server's reload client. Absent in a build. */\n readonly shellExtra?: string\n /** Envelope files a local host adds (see `LocalHost.envelopeEntries`). */\n readonly envelopeEntries?: Readonly<Record<string, unknown>>\n /** A compile cache to reuse across builds; default: one on disk under `.pwf/build-cache/`. */\n readonly cache?: CompileCache\n /** Folder of the on-disk compile cache; `null` keeps it in memory only. */\n readonly cacheDir?: string | null\n /** `publish.json`'s `publishedAt`; default now. */\n readonly publishedAt?: string\n /** Runner and renderer bundles; default: the installed engine packages. */\n readonly runtime?: RuntimeAssets\n /** Pack builds read by an earlier build of the same session. */\n readonly packMemo?: PackMemo\n}\n\nexport interface AssembleStats {\n readonly pages: number\n /** Entries in `blocks/manifest.json`. */\n readonly blocks: number\n /** Entries in `engines/manifest.json`. */\n readonly engines: number\n /** Atoms of a compiled kind across the app and its packs. */\n readonly atoms: number\n /** Of those, how many have a module (compiled or shipped precompiled). */\n readonly compiled: number\n /** Of the compiled, how many came from the cache. */\n readonly fromCache: number\n readonly failed: number\n}\n\nexport interface AssembleResult {\n /** No diagnostic is an error. */\n readonly ok: boolean\n /** The served folder: POSIX path → bytes. Filled as far as the build got, even when not `ok`. */\n readonly files: Map<string, Uint8Array>\n readonly diagnostics: readonly AssembleDiagnostic[]\n readonly stats: AssembleStats\n readonly ms: number\n}\n\nconst enc = new TextEncoder()\nconst json = (value: unknown): Uint8Array => enc.encode(JSON.stringify(value, null, 2))\nconst hexOf = (hash: string): string => hash.replace(/^sha256:/, '')\n\n/** Every name a block atom answers to in a receipt: its id, its manifest name, and its short forms. */\nexport function blockNames(atom: Pick<CompiledAtom, 'id' | 'manifest'>): string[] {\n const names = new Set<string>([atom.id])\n const name = atom.manifest?.['name']\n if (typeof name === 'string' && name) names.add(name)\n const dot = atom.id.indexOf('.')\n if (dot >= 0) names.add(atom.id.slice(dot + 1))\n names.add(atom.id.slice(atom.id.lastIndexOf('.') + 1))\n return [...names]\n}\n\nfunction titleOf(plan: unknown, id: string): string {\n if (isRecord(plan) && typeof plan['title'] === 'string' && plan['title'].trim()) return plan['title'].trim()\n const last = id.split('/').pop() ?? id\n return last\n .split(/[-_]+/)\n .filter(Boolean)\n .map((w) => (w[0] as string).toUpperCase() + w.slice(1))\n .join(' ')\n}\n\nfunction atomLabel(atom: CompiledAtom): string {\n return atom.source.origin === 'app' ? atom.id : `${atom.id} (${atom.source.id}@${atom.source.version})`\n}\n\nconst EMPTY_STATS: AssembleStats = { pages: 0, blocks: 0, engines: 0, atoms: 0, compiled: 0, fromCache: 0, failed: 0 }\n\n/** Assembles the pwapp for the app folder at `root`. */\nexport async function assemblePwapp(root: string, options: AssembleOptions = {}): Promise<AssembleResult> {\n const started = performance.now()\n const diagnostics: AssembleDiagnostic[] = []\n const files = new Map<string, Uint8Array>()\n const done = (stats: AssembleStats): AssembleResult => {\n const sorted = sortDiagnostics(diagnostics)\n return { ok: !hasErrors(sorted), files, diagnostics: sorted, stats, ms: Math.round(performance.now() - started) }\n }\n\n const sources = await loadSources(root, options.packMemo)\n diagnostics.push(...sources.diagnostics)\n const { app, identity } = sources\n if (!app || !identity) return done(EMPTY_STATS)\n if (!identity.app) {\n diagnostics.push({\n severity: 'error',\n code: 'NOT_AN_APP',\n message: 'pwpack.json declares no \"app\" member, so this folder is a pack, not an app — there is nothing to run',\n path: 'pwpack.json',\n })\n return done(EMPTY_STATS)\n }\n\n // ── pages ────────────────────────────────────────────────────────\n const reported = new Set(diagnostics.map((d) => d.path))\n const pages: Page[] = []\n for (const key of identity.app.surfaceOrder) {\n const path = `app/surfaces/${key}.receipt.json`\n const bytes = app.folder.files.get(path)\n const value = jsonOf(app.folder, path)\n if (!bytes) {\n if (!reported.has(path)) {\n diagnostics.push({ severity: 'error', code: 'RECEIPT_MISSING', message: `page \"${key}\" is in app.surfaceOrder but ${path} does not exist`, path })\n }\n continue\n }\n if (value instanceof Error || !isRecord(value) || !Array.isArray(value['blocks'])) {\n diagnostics.push({\n severity: 'error',\n code: 'RECEIPT_INVALID',\n message: value instanceof Error ? `not JSON: ${value.message}` : 'a receipt is an object with a \"blocks\" array',\n path,\n })\n continue\n }\n pages.push({ key, path, bytes, receipt: value })\n }\n const entry = identity.app.entry\n if (!identity.app.surfaceOrder.includes(entry)) {\n diagnostics.push({ severity: 'error', code: 'ENTRY_NOT_A_PAGE', message: `app.entry \"${entry}\" is not in app.surfaceOrder`, path: 'pwpack.json' })\n }\n\n // ── definitions ──────────────────────────────────────────────────\n const definition = (name: string): unknown => {\n const path = `app/definitions/${name}.json`\n const value = jsonOf(app.folder, path)\n if (value instanceof Error) {\n diagnostics.push({ severity: 'error', code: 'DEFINITION_INVALID', message: `not JSON: ${value.message}`, path })\n return undefined\n }\n return value\n }\n const navigationDoc = definition('navigation')\n const appearanceDoc = definition('appearance')\n const connectionsDoc = definition('connections')\n const authDoc = definition('auth')\n const plan = jsonOf(app.folder, 'app/plan.json')\n\n // ── compile ──────────────────────────────────────────────────────\n const all: SourcePack[] = [app, ...sources.packs]\n const cache = options.cache ?? createCompileCache(options.cacheDir === undefined ? join(root, PWF_DIR, 'build-cache') : options.cacheDir)\n const atoms = await compileSources(all, cache)\n\n // ── themes ───────────────────────────────────────────────────────\n const themes: ThemeAtom[] = []\n for (const source of all) {\n for (const ref of source.validation.atoms) {\n if (ref.kind !== 'ui-appearance') continue\n const theme = themeAtomOf(ref.id, jsonOf(source.folder, ref.relPath), source.label || source.id)\n if (theme) themes.push(theme)\n }\n }\n const appearance = resolveAppearance(appearanceDoc, themes, diagnostics)\n\n // ── blocks ───────────────────────────────────────────────────────\n const byName = new Map<string, CompiledAtom[]>()\n for (const atom of atoms) {\n if (atom.kind !== 'block') continue\n for (const name of blockNames(atom)) {\n const list = byName.get(name) ?? []\n if (!list.includes(atom)) list.push(atom)\n byName.set(name, list)\n }\n }\n const used = new Set<CompiledAtom>()\n const blockManifest: BlockManifestEntry[] = []\n const modules = new Map<string, Uint8Array>()\n const firstUse = new Map<string, Page>()\n for (const page of pages) for (const t of blockTypesOf(page.receipt)) if (!firstUse.has(t)) firstUse.set(t, page)\n for (const [type, page] of [...firstUse].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))) {\n const candidates = byName.get(type) ?? []\n const atom = candidates[0]\n if (!atom) {\n diagnostics.push({\n severity: 'error',\n code: 'UNKNOWN_BLOCK_TYPE',\n message: `page \"${page.key}\" places a block of type \"${type}\", which neither the app nor its pinned packs define`,\n path: page.path,\n })\n continue\n }\n used.add(atom)\n if (candidates.length > 1) {\n diagnostics.push({\n severity: 'info',\n code: 'BLOCK_TYPE_SHADOWED',\n message: `block type \"${type}\" resolves to ${atomLabel(atom)}; also defined by ${candidates.slice(1).map(atomLabel).join(', ')}`,\n path: page.path,\n })\n }\n if (!atom.code || !atom.hash) {\n diagnostics.push({\n severity: 'error',\n code: 'BLOCK_NOT_COMPILED',\n message: `block type \"${type}\" (${atomLabel(atom)}) did not compile (see the compile errors reported for ${atom.relPath})`,\n path: page.path,\n })\n continue\n }\n const file = `blocks/${moduleFileName(type, hexOf(atom.hash))}`\n modules.set(file, enc.encode(atom.code))\n blockManifest.push({\n blockType: type,\n moduleUrl: file,\n integrity: hexOf(atom.hash),\n publisher: publisherOf(atom.source.id),\n publisherVerified: false,\n definition: { name: type },\n })\n }\n\n // ── engines ──────────────────────────────────────────────────────\n const connections = buildConnections(connectionsDoc, diagnostics)\n const engineManifest: EngineManifestEntry[] = []\n for (const conn of connections?.connections ?? []) {\n const name = conn['name'] as string\n const wanted = typeof conn['connector'] === 'string' ? conn['connector'] : name\n const atom = atoms.find(\n (a) => (a.kind === 'connector' || a.kind === 'provider') && a.manifest?.['connection'] === wanted\n )\n if (!atom) continue\n used.add(atom)\n const kind = atom.kind as 'connector' | 'provider'\n if (atom.code && atom.hash) {\n const file = `engines/${moduleFileName(atom.id, hexOf(atom.hash))}`\n modules.set(file, enc.encode(atom.code))\n engineManifest.push({ connection: name, atomId: atom.id, kind, moduleUrl: file, integrity: hexOf(atom.hash) })\n } else if (atom.manifest?.['engine'] !== undefined) {\n engineManifest.push({ connection: name, atomId: atom.id, kind, engine: atom.manifest['engine'] })\n } else if (atom.status === 'failed') {\n diagnostics.push({\n severity: 'warning',\n code: 'ENGINE_NOT_COMPILED',\n message: `connection \"${name}\": engine ${atomLabel(atom)} did not compile, so its panels fall back to sample data`,\n path: 'app/definitions/connections.json',\n })\n }\n }\n\n // ── atom diagnostics ─────────────────────────────────────────────\n // The app's own atoms and the atoms it uses speak at full volume; an unused\n // atom of a pinned pack cannot break this app, so its errors are warnings.\n for (const atom of atoms) {\n const loud = atom.source.origin === 'app' || used.has(atom)\n for (const d of atom.diagnostics) {\n const engineDegrades = atomKindMeta(atom.kind).onFailure === 'degrade'\n diagnostics.push(loud && !engineDegrades ? d : d.severity === 'error' ? { ...d, severity: 'warning' } : d)\n }\n }\n\n // ── the served folder ────────────────────────────────────────────\n const runtime = options.runtime ?? (await loadRuntimeAssets())\n const config = appearanceConfigOf(appearance)\n const title = titleOf(plan, identity.id)\n const locale = isRecord(plan) && typeof plan['locale'] === 'string' ? plan['locale'] : undefined\n const shell = (csp?: string): Uint8Array =>\n enc.encode(\n buildShellHtml({\n title,\n ...(locale !== undefined ? { lang: locale } : {}),\n ...(config ? { appearance: config } : {}),\n ...(options.shellExtra !== undefined ? { bodyExtra: options.shellExtra } : {}),\n ...(csp !== undefined ? { csp } : {}),\n })\n )\n // Written again with its policy once everything the policy reads is known.\n files.set('index.html', shell())\n files.set('assets/seed.js', enc.encode(seedScript()))\n files.set('assets/boot.js', enc.encode(bootScript(runnerLocaleOf(locale))))\n files.set('assets/runner.js', enc.encode(runtime.runnerJs))\n if (runtime.runnerMap !== undefined) files.set('assets/runner.js.map', enc.encode(runtime.runnerMap))\n files.set('assets/embed.js', enc.encode(runtime.embedJs))\n files.set('assets/theme.css', enc.encode(buildThemeCss(config)))\n for (const [name, source] of await shimsFor(runtime.embedJs)) files.set(`shims/${name}`, enc.encode(source))\n for (const [file, bytes] of [...modules].sort((a, b) => (a[0] < b[0] ? -1 : 1))) files.set(file, bytes)\n files.set('blocks/manifest.json', json(blockManifest))\n files.set('engines/manifest.json', json(engineManifest))\n\n files.set('pwpack/project.json', json(buildProjectJson({ id: identity.id, title, entry, pages, auth: authDoc })))\n for (const page of pages) files.set(`pwpack/surfaces/${page.key}.receipt.json`, page.bytes)\n files.set('pwpack/navigation.json', json(isRecord(navigationDoc) ? navigationDoc : deriveNavigation(pages, entry)))\n if (appearance) files.set('pwpack/appearance.json', json(appearance))\n // Always written: the runner asks for it on every boot, and an app that\n // declares no connection should not cost the visitor a 404 in the console.\n files.set('pwpack/connections.json', json(connections ?? { connections: [] }))\n const media = collectMedia(app.folder, diagnostics)\n for (const [file, bytes] of media.files) files.set(`pwpack/${file}`, bytes)\n mergeEnvelopeEntries(files, options.envelopeEntries, diagnostics)\n // After the host's entries, so a receipt the host rewrote gets its media too.\n for (const page of pages) {\n const path = `pwpack/surfaces/${page.key}.receipt.json`\n const filled = fillMediaSlots(files.get(path) as Uint8Array, media.slots.get(page.key))\n if (filled) files.set(path, filled)\n }\n\n // ── the Content Security Policy ──────────────────────────────────\n const servedReceipts = new Map<string, unknown>()\n for (const page of pages) {\n const bytes = files.get(`pwpack/surfaces/${page.key}.receipt.json`)\n try {\n servedReceipts.set(page.key, bytes ? JSON.parse(new TextDecoder().decode(bytes)) : page.receipt)\n } catch {\n servedReceipts.set(page.key, page.receipt)\n }\n }\n const envelopeAuthBytes = files.get('pwpack/auth.json')\n const csp = deriveCsp({\n importMapText: importMapText(),\n receipts: servedReceipts,\n connections,\n engines: engineManifest,\n envelopeAuth: envelopeAuthBytes ? JSON.parse(new TextDecoder().decode(envelopeAuthBytes)) : undefined,\n authDefinition: authDoc,\n media: media.records,\n webfontHref: webfontStylesheetHref(themeWebfonts(config)),\n })\n files.set('index.html', shell(csp.meta))\n files.set('_headers', enc.encode(headersFile(csp.header)))\n\n const publishedAtoms = atoms\n .filter((a) => used.has(a))\n .map((a) => {\n const module = blockManifest.find((b) => b.integrity === (a.hash ? hexOf(a.hash) : ''))?.moduleUrl ??\n engineManifest.find((e) => e.atomId === a.id && e.moduleUrl)?.moduleUrl\n return {\n kind: a.kind,\n id: a.id,\n hash: a.sourceHash,\n ...(module && a.hash ? { module, integrity: hexOf(a.hash) } : {}),\n }\n })\n files.set(\n 'publish.json',\n json({\n formatVersion: '1.0',\n app: { id: identity.id, version: identity.version },\n liveVersion: identity.version,\n publishedAt: options.publishedAt ?? new Date().toISOString(),\n packs: sources.packs.map((p) => ({ id: p.id, version: p.version, hash: p.pinnedHash ?? hashPack(p.folder) })),\n atoms: publishedAtoms,\n ...(media.records.length > 0 ? { media: media.records } : {}),\n csp: { policy: csp.header, ...(csp.unresolved.length > 0 ? { unresolved: csp.unresolved } : {}) },\n })\n )\n\n const compiled = atoms.filter((a) => a.status === 'compiled' || a.status === 'precompiled')\n return done({\n pages: pages.length,\n blocks: blockManifest.length,\n engines: engineManifest.length,\n atoms: atoms.length,\n compiled: compiled.length,\n fromCache: compiled.filter((a) => a.fromCache).length,\n failed: atoms.filter((a) => a.status === 'failed').length,\n })\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type CompileResult } from '@pwfabric/compiler';
|
|
2
|
+
import { type AtomKind } from '@pwfabric/contracts';
|
|
3
|
+
import type { AssembleDiagnostic } from './diagnostics.js';
|
|
4
|
+
import { type SourcePack } from './sources.js';
|
|
5
|
+
/** Kinds that carry a compiled module. */
|
|
6
|
+
export declare const COMPILED_KINDS: ReadonlySet<AtomKind>;
|
|
7
|
+
/** One atom after the compile step. */
|
|
8
|
+
export interface CompiledAtom {
|
|
9
|
+
readonly source: SourcePack;
|
|
10
|
+
readonly kind: AtomKind;
|
|
11
|
+
readonly id: string;
|
|
12
|
+
/** Path of the atom's file inside its source folder. */
|
|
13
|
+
readonly relPath: string;
|
|
14
|
+
/** `sha256:<hex>` of that file. */
|
|
15
|
+
readonly sourceHash: string;
|
|
16
|
+
/**
|
|
17
|
+
* `compiled` — a module was produced; `precompiled` — the pack shipped one;
|
|
18
|
+
* `declared` — an engine that is data, nothing to compile; `failed`.
|
|
19
|
+
*/
|
|
20
|
+
readonly status: 'compiled' | 'precompiled' | 'declared' | 'failed';
|
|
21
|
+
readonly code?: string;
|
|
22
|
+
/** `sha256:<hex>` of `code`. */
|
|
23
|
+
readonly hash?: string;
|
|
24
|
+
readonly diagnostics: readonly AssembleDiagnostic[];
|
|
25
|
+
readonly fromCache: boolean;
|
|
26
|
+
/** The atom's manifest (`manifest.json` next to it, or the knowledge file itself). */
|
|
27
|
+
readonly manifest?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/** What the cache stores per key. */
|
|
30
|
+
export interface CachedCompile {
|
|
31
|
+
readonly ok: boolean;
|
|
32
|
+
readonly code: string;
|
|
33
|
+
readonly hash: string;
|
|
34
|
+
readonly diagnostics: CompileResult['diagnostics'];
|
|
35
|
+
}
|
|
36
|
+
export interface CompileCache {
|
|
37
|
+
get(key: string): Promise<CachedCompile | undefined>;
|
|
38
|
+
set(key: string, value: CachedCompile): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A cache in `dir` (one JSON file per key) with an in-memory layer in front.
|
|
42
|
+
* Disk failures are not build failures: a cache that cannot be read or
|
|
43
|
+
* written just compiles again.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createCompileCache(dir: string | null): CompileCache;
|
|
46
|
+
export declare function sha256(bytes: Uint8Array | string): string;
|
|
47
|
+
/** The root under which a source keeps its atoms: `atoms/` in the canonical layout, `` in the rootless one. */
|
|
48
|
+
export declare function atomRootOf(source: SourcePack): string;
|
|
49
|
+
/** The compiler's view of a source: atom files keyed by their path below the atom root. */
|
|
50
|
+
export declare function compileTreeOf(source: SourcePack): Record<string, Uint8Array>;
|
|
51
|
+
/** Runs `fn` over `items` with at most `limit` in flight, keeping order. */
|
|
52
|
+
export declare function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]>;
|
|
53
|
+
/** Compiles every compiled-kind atom of every source. */
|
|
54
|
+
export declare function compileSources(sources: readonly SourcePack[], cache: CompileCache, concurrency?: number): Promise<CompiledAtom[]>;
|