@vmz/vmz 0.0.4 → 0.1.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.
Files changed (55) hide show
  1. package/dist/build-assemble.d.ts +52 -0
  2. package/dist/build-assemble.js +192 -0
  3. package/dist/cdn-policy.d.ts +22 -4
  4. package/dist/cdn-policy.js +109 -13
  5. package/dist/cli.js +122 -27
  6. package/dist/content-addressed-assets.js +2 -1
  7. package/dist/delivery-profile.d.ts +84 -0
  8. package/dist/delivery-profile.js +348 -0
  9. package/dist/dev-session.d.ts +6 -0
  10. package/dist/dev-session.js +167 -40
  11. package/dist/document-build.js +33 -32
  12. package/dist/document-cmd.js +2 -9
  13. package/dist/document-enrich.js +8 -0
  14. package/dist/document-integrate.js +10 -17
  15. package/dist/embedded-packaging.d.ts +22 -0
  16. package/dist/embedded-packaging.js +109 -0
  17. package/dist/index.d.ts +26 -1
  18. package/dist/index.js +69 -2
  19. package/dist/locale-check.js +39 -66
  20. package/dist/locale-cmd.js +12 -44
  21. package/dist/locale-route-emit.d.ts +37 -0
  22. package/dist/locale-route-emit.js +109 -0
  23. package/dist/locale-router.d.ts +20 -0
  24. package/dist/locale-router.js +68 -0
  25. package/dist/log.d.ts +2 -2
  26. package/dist/log.js +11 -3
  27. package/dist/mini-host.d.ts +47 -0
  28. package/dist/mini-host.js +202 -0
  29. package/dist/native-addon.d.ts +9 -0
  30. package/dist/native-addon.js +84 -0
  31. package/dist/pack-client-packages.d.ts +25 -0
  32. package/dist/pack-client-packages.js +399 -0
  33. package/dist/pack.d.ts +58 -0
  34. package/dist/pack.js +123 -0
  35. package/dist/plugin-host.d.ts +1 -1
  36. package/dist/plugin-host.js +2 -2
  37. package/dist/pretty-json.d.ts +19 -0
  38. package/dist/pretty-json.js +43 -0
  39. package/dist/production-observability.js +3 -2
  40. package/dist/production-test-pack.d.ts +0 -14
  41. package/dist/production-test-pack.js +27 -31
  42. package/dist/release-pack.js +17 -21
  43. package/dist/route-path.d.ts +35 -0
  44. package/dist/route-path.js +77 -0
  45. package/dist/server-artifact.d.ts +140 -0
  46. package/dist/server-artifact.js +204 -0
  47. package/dist/server-language-backend.d.ts +89 -0
  48. package/dist/server-language-backend.js +121 -0
  49. package/dist/site-delivery.js +3 -2
  50. package/dist/static-emit.d.ts +10 -1
  51. package/dist/static-emit.js +192 -97
  52. package/dist/test-cmd.js +2 -1
  53. package/dist/wechat-packaging.d.ts +22 -0
  54. package/dist/wechat-packaging.js +59 -0
  55. package/package.json +12 -12
@@ -0,0 +1,348 @@
1
+ /**
2
+ * B0 — Delivery profile authoring normalize + CLI --profile resolve.
3
+ * Pure data only; expands legacy site-delivery sugar into profiles[default].
4
+ */
5
+ // @ts-nocheck
6
+ import crypto from 'node:crypto';
7
+ export const DELIVERY_PROFILE_AUTHORING_SCHEMA = 'vmz.delivery.authoring.v0';
8
+ export const BUILD_PROFILE_SELECTION_SCHEMA = 'vmz.build.profile_selection.v0';
9
+ /** Browser-era assembly kinds (04 B5). */
10
+ export const ASSEMBLIES = Object.freeze(['local-static', 'static-cdn', 'server-host', 'cdn+server', 'rust-embedded']);
11
+ export const SERVER_RUNTIMES = Object.freeze(['node', 'worker', 'deno', 'bun', 'rust-host']);
12
+ /** Official built-in aliases when not overridden in config. */
13
+ export const BUILTIN_PROFILES = Object.freeze({
14
+ 'web-client': { host: 'browser', assembly: 'local-static' },
15
+ 'web-static': { host: 'browser', assembly: 'static-cdn' },
16
+ 'web-ssr': { host: 'browser', assembly: 'server-host', serverRuntime: 'node' },
17
+ 'web-hybrid': { host: 'browser', assembly: 'cdn+server', serverRuntime: 'node' },
18
+ });
19
+ function isPlainObject(v) {
20
+ return v != null && typeof v === 'object' && !Array.isArray(v);
21
+ }
22
+ export function pickSiteAuthoring(raw) {
23
+ if (!isPlainObject(raw))
24
+ return null;
25
+ if (!Array.isArray(raw.sources) || raw.sources.length < 1)
26
+ return null;
27
+ if (typeof raw.artifact !== 'string' || !String(raw.artifact).trim())
28
+ return null;
29
+ const site = {
30
+ artifact: String(raw.artifact),
31
+ sources: raw.sources,
32
+ };
33
+ for (const k of [
34
+ 'siteId',
35
+ 'resolution',
36
+ 'activation',
37
+ 'expectedCompatibility',
38
+ 'failure',
39
+ 'failurePolicy',
40
+ 'update',
41
+ 'updatePolicy',
42
+ 'rollback',
43
+ 'rollbackPolicy',
44
+ 'security',
45
+ 'securityPolicy',
46
+ ]) {
47
+ if (raw[k] !== undefined)
48
+ site[k] = raw[k];
49
+ }
50
+ return site;
51
+ }
52
+ /**
53
+ * `delivery.packaging.wechat` — vendor identity, not WeChat JSON / wx APIs.
54
+ * @param {unknown} raw
55
+ * @param {Array<{ code: string, message: string }>} diagnostics
56
+ */
57
+ export function pickDeliveryPackaging(raw, diagnostics) {
58
+ if (!isPlainObject(raw) || raw.packaging == null)
59
+ return null;
60
+ if (!isPlainObject(raw.packaging)) {
61
+ diagnostics.push({ code: 'delivery.packaging', message: 'delivery.packaging must be an object' });
62
+ return null;
63
+ }
64
+ for (const key of Object.keys(raw.packaging)) {
65
+ if (key !== 'wechat') {
66
+ diagnostics.push({
67
+ code: 'delivery.packaging.vendor',
68
+ message: `delivery.packaging.${key} is not a known vendor (wechat)`,
69
+ });
70
+ }
71
+ }
72
+ const wechat = raw.packaging.wechat;
73
+ if (wechat == null)
74
+ return {};
75
+ if (!isPlainObject(wechat)) {
76
+ diagnostics.push({
77
+ code: 'delivery.packaging.wechat',
78
+ message: 'delivery.packaging.wechat must be an object',
79
+ });
80
+ return null;
81
+ }
82
+ for (const [k, v] of Object.entries(wechat)) {
83
+ if (typeof v === 'function') {
84
+ diagnostics.push({
85
+ code: 'delivery.packaging.executable',
86
+ message: `delivery.packaging.wechat.${k} must be pure data (no functions)`,
87
+ });
88
+ continue;
89
+ }
90
+ if (k !== 'appId' && k !== 'projectName' && k !== 'title') {
91
+ diagnostics.push({
92
+ code: 'delivery.packaging.wechat.field',
93
+ message: `delivery.packaging.wechat.${k} is not a known field (appId|projectName|title)`,
94
+ });
95
+ }
96
+ else if (v != null && typeof v !== 'string') {
97
+ diagnostics.push({
98
+ code: 'delivery.packaging.wechat.type',
99
+ message: `delivery.packaging.wechat.${k} must be a string`,
100
+ });
101
+ }
102
+ }
103
+ const out = {};
104
+ if (typeof wechat.appId === 'string' && wechat.appId.trim())
105
+ out.appId = wechat.appId.trim();
106
+ if (typeof wechat.projectName === 'string' && wechat.projectName.trim()) {
107
+ out.projectName = wechat.projectName.trim();
108
+ }
109
+ if (typeof wechat.title === 'string' && wechat.title.trim())
110
+ out.title = wechat.title.trim();
111
+ return { wechat: out };
112
+ }
113
+ function normalizeProfileEntry(entry, id, diagnostics) {
114
+ if (!isPlainObject(entry)) {
115
+ diagnostics.push({ code: 'delivery.profile.invalid', message: `profiles.${id} must be an object` });
116
+ return null;
117
+ }
118
+ const host = String(entry.host || 'browser');
119
+ if (host !== 'browser') {
120
+ diagnostics.push({
121
+ code: 'delivery.profile.host',
122
+ message: `profiles.${id}.host: only 'browser' is supported before Browser Production (got ${host})`,
123
+ });
124
+ }
125
+ const assembly = String(entry.assembly || '').trim();
126
+ if (!ASSEMBLIES.includes(assembly)) {
127
+ diagnostics.push({
128
+ code: 'delivery.profile.assembly',
129
+ message: `profiles.${id}.assembly must be one of ${ASSEMBLIES.join('|')} (got ${assembly || '(empty)'})`,
130
+ });
131
+ return null;
132
+ }
133
+ let serverRuntime = null;
134
+ if (assembly === 'server-host' || assembly === 'cdn+server') {
135
+ serverRuntime = String(entry.serverRuntime || 'node');
136
+ if (!SERVER_RUNTIMES.includes(serverRuntime)) {
137
+ diagnostics.push({
138
+ code: 'delivery.profile.serverRuntime',
139
+ message: `profiles.${id}.serverRuntime must be one of ${SERVER_RUNTIMES.join('|')}`,
140
+ });
141
+ }
142
+ }
143
+ let sources = null;
144
+ if (entry.sources != null) {
145
+ if (isPlainObject(entry.sources) && Array.isArray(entry.sources.sources)) {
146
+ sources = pickSiteAuthoring(entry.sources);
147
+ }
148
+ else if (Array.isArray(entry.sources)) {
149
+ sources = pickSiteAuthoring({
150
+ artifact: entry.artifact || entry.sourcesArtifact || id,
151
+ sources: entry.sources,
152
+ resolution: entry.resolution,
153
+ activation: entry.activation,
154
+ });
155
+ }
156
+ else {
157
+ diagnostics.push({
158
+ code: 'delivery.profile.sources',
159
+ message: `profiles.${id}.sources must be defineSite({...}) or a sources array with artifact`,
160
+ });
161
+ }
162
+ if (entry.sources != null && sources == null) {
163
+ const already = diagnostics.some((d) => String(d.message || '').includes(`profiles.${id}`));
164
+ if (!already) {
165
+ diagnostics.push({
166
+ code: 'delivery.profile.sources.artifact',
167
+ message: `profiles.${id} site sources require artifact string`,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ return {
173
+ id,
174
+ host: 'browser',
175
+ assembly,
176
+ serverRuntime,
177
+ sources,
178
+ };
179
+ }
180
+ export function normalizeDeliveryAuthoring(raw) {
181
+ const diagnostics = [];
182
+ if (raw == null) {
183
+ const profiles = { ...BUILTIN_PROFILES };
184
+ const normalized = {};
185
+ for (const [id, entry] of Object.entries(profiles)) {
186
+ const n = normalizeProfileEntry(entry, id, diagnostics);
187
+ if (n)
188
+ normalized[id] = n;
189
+ }
190
+ const table = {
191
+ schema: DELIVERY_PROFILE_AUTHORING_SCHEMA,
192
+ default: 'web-ssr',
193
+ profiles: normalized,
194
+ sugar: false,
195
+ };
196
+ table.digest = sha256Hex(canonicalJson(table));
197
+ return { ok: true, table };
198
+ }
199
+ if (!isPlainObject(raw)) {
200
+ return {
201
+ ok: false,
202
+ diagnostics: [{ code: 'delivery.invalid', message: 'delivery must be a plain object' }],
203
+ };
204
+ }
205
+ let profileInputs = {};
206
+ let defaultId = '';
207
+ let sugar = false;
208
+ if (isPlainObject(raw.profiles)) {
209
+ defaultId = String(raw.default || '').trim();
210
+ profileInputs = { ...BUILTIN_PROFILES, ...raw.profiles };
211
+ if (!defaultId) {
212
+ const keys = Object.keys(raw.profiles);
213
+ defaultId = keys[0] || 'web-ssr';
214
+ }
215
+ }
216
+ else if (Array.isArray(raw.sources) || raw.artifact != null || raw.assembly != null) {
217
+ sugar = true;
218
+ const site = pickSiteAuthoring(raw);
219
+ defaultId = String(raw.default || raw.artifact || 'web-ssr').trim() || 'web-ssr';
220
+ const assembly = typeof raw.assembly === 'string' && ASSEMBLIES.includes(raw.assembly) ? raw.assembly : site ? 'rust-embedded' : 'server-host';
221
+ profileInputs = {
222
+ ...BUILTIN_PROFILES,
223
+ [defaultId]: {
224
+ host: raw.host || 'browser',
225
+ assembly,
226
+ serverRuntime: raw.serverRuntime || 'node',
227
+ ...(site
228
+ ? {
229
+ artifact: site.artifact,
230
+ sources: site.sources,
231
+ resolution: site.resolution,
232
+ activation: site.activation,
233
+ expectedCompatibility: site.expectedCompatibility,
234
+ failure: site.failure,
235
+ failurePolicy: site.failurePolicy,
236
+ update: site.update,
237
+ updatePolicy: site.updatePolicy,
238
+ rollback: site.rollback,
239
+ rollbackPolicy: site.rollbackPolicy,
240
+ security: site.security,
241
+ securityPolicy: site.securityPolicy,
242
+ }
243
+ : {}),
244
+ },
245
+ };
246
+ }
247
+ else if (isPlainObject(raw.packaging)) {
248
+ defaultId = String(raw.default || 'web-ssr').trim() || 'web-ssr';
249
+ profileInputs = { ...BUILTIN_PROFILES };
250
+ }
251
+ else {
252
+ return {
253
+ ok: false,
254
+ diagnostics: [
255
+ {
256
+ code: 'delivery.shape',
257
+ message: 'delivery must declare profiles{} or legacy { artifact, sources }',
258
+ },
259
+ ],
260
+ };
261
+ }
262
+ const profiles = {};
263
+ for (const [id, entry] of Object.entries(profileInputs)) {
264
+ const n = normalizeProfileEntry(entry, id, diagnostics);
265
+ if (n)
266
+ profiles[id] = n;
267
+ }
268
+ if (!profiles[defaultId]) {
269
+ diagnostics.push({
270
+ code: 'delivery.default',
271
+ message: `delivery.default '${defaultId}' is not a known profile`,
272
+ });
273
+ }
274
+ if (diagnostics.length)
275
+ return { ok: false, diagnostics };
276
+ const packaging = pickDeliveryPackaging(raw, diagnostics);
277
+ if (diagnostics.length)
278
+ return { ok: false, diagnostics };
279
+ const table = {
280
+ schema: DELIVERY_PROFILE_AUTHORING_SCHEMA,
281
+ default: defaultId,
282
+ profiles,
283
+ sugar,
284
+ ...(packaging ? { packaging } : {}),
285
+ };
286
+ table.digest = sha256Hex(canonicalJson(table));
287
+ return { ok: true, table };
288
+ }
289
+ export function selectBuildProfile(table, cliProfile = '') {
290
+ const id = String(cliProfile || '').trim() || table.default;
291
+ const profile = table.profiles[id];
292
+ if (!profile) {
293
+ return {
294
+ ok: false,
295
+ diagnostics: [
296
+ {
297
+ code: 'delivery.profile.unknown',
298
+ message: `unknown build --profile ${id} (known: ${Object.keys(table.profiles).join(', ')})`,
299
+ },
300
+ ],
301
+ };
302
+ }
303
+ const selection = {
304
+ schema: BUILD_PROFILE_SELECTION_SCHEMA,
305
+ profileId: id,
306
+ host: profile.host,
307
+ assembly: profile.assembly,
308
+ serverRuntime: profile.serverRuntime,
309
+ hasSiteSources: Boolean(profile.sources),
310
+ authoringDigest: table.digest,
311
+ fromCli: Boolean(String(cliProfile || '').trim()),
312
+ };
313
+ selection.digest = sha256Hex(canonicalJson(selection));
314
+ return { ok: true, selection, profile };
315
+ }
316
+ export function semanticIdsForAssembly(assembly) {
317
+ switch (assembly) {
318
+ case 'static-cdn':
319
+ return ['static-delivery', 'asset-graph'];
320
+ case 'server-host':
321
+ return ['server-host', 'asset-graph'];
322
+ case 'cdn+server':
323
+ return ['server-host', 'static-delivery', 'asset-graph'];
324
+ case 'rust-embedded':
325
+ return ['site-fallback', 'asset-graph'];
326
+ case 'local-static':
327
+ return ['asset-graph'];
328
+ default:
329
+ return [];
330
+ }
331
+ }
332
+ export function canonicalJson(value) {
333
+ return JSON.stringify(sortKeys(value));
334
+ }
335
+ function sortKeys(value) {
336
+ if (Array.isArray(value))
337
+ return value.map(sortKeys);
338
+ if (value && typeof value === 'object') {
339
+ const out = {};
340
+ for (const k of Object.keys(value).sort())
341
+ out[k] = sortKeys(value[k]);
342
+ return out;
343
+ }
344
+ return value;
345
+ }
346
+ export function sha256Hex(text) {
347
+ return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
348
+ }
@@ -3,6 +3,11 @@
3
3
  *
4
4
  * Rebuilds go through the N-API `Workspace` — never spawn `cargo` / `vmz-tools`.
5
5
  * session: only dirty leaves are marked; Workspace emits affected deployment units.
6
+ *
7
+ * Reload policy (Vite-like — author never hand-restarts):
8
+ * 1. Prefer in-process soft reload (`POST /__vmz/reload`) with transitive `?t=` via serve-host hook.
9
+ * 2. Soft reload only re-imports affected pages unless shared `lib/` / full rebuild.
10
+ * 3. Soft reload failure → **auto-respawn** serve-host (fresh ESM graph), not "keep broken host".
6
11
  */
7
12
  /**
8
13
  * @typedef {object} DevSessionOptions
@@ -11,6 +16,7 @@
11
16
  * @property {string} [host]
12
17
  * @property {number} [port]
13
18
  * @property {number} [pollMs]
19
+ * @property {'browser' | 'mini-program-wechat'} [target]
14
20
  * @property {AbortSignal} [signal]
15
21
  * @property {typeof createWorkspace} [createWorkspaceFn]
16
22
  * @property {(opts: { project: string, outDir: string, host: string, port: number }) => import('node:child_process').ChildProcess} [spawnHostFn]