@kurot/cli 3.7.0 → 3.8.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 (57) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/README.md +23 -11
  3. package/dist/core/config-watcher.d.ts +8 -0
  4. package/dist/core/config-watcher.d.ts.map +1 -0
  5. package/dist/core/config-watcher.js +63 -0
  6. package/dist/core/config-watcher.js.map +1 -0
  7. package/dist/core/config.d.ts +9 -3
  8. package/dist/core/config.d.ts.map +1 -1
  9. package/dist/core/config.js +23 -6
  10. package/dist/core/config.js.map +1 -1
  11. package/dist/core/dev-server.d.ts.map +1 -1
  12. package/dist/core/dev-server.js +2 -0
  13. package/dist/core/dev-server.js.map +1 -1
  14. package/dist/core/diagnostics/codes.d.ts +2 -0
  15. package/dist/core/diagnostics/codes.d.ts.map +1 -1
  16. package/dist/core/diagnostics/codes.js +2 -0
  17. package/dist/core/diagnostics/codes.js.map +1 -1
  18. package/dist/core/diagnostics/output.d.ts +1 -1
  19. package/dist/core/diagnostics/output.d.ts.map +1 -1
  20. package/dist/core/plugins/generate-html.d.ts +7 -1
  21. package/dist/core/plugins/generate-html.d.ts.map +1 -1
  22. package/dist/core/plugins/generate-html.js +20 -4
  23. package/dist/core/plugins/generate-html.js.map +1 -1
  24. package/dist/core/plugins/index.d.ts +1 -1
  25. package/dist/core/plugins/index.d.ts.map +1 -1
  26. package/dist/core/plugins/index.js +3 -3
  27. package/dist/core/plugins/index.js.map +1 -1
  28. package/dist/core/plugins/resource-version-css.d.ts +7 -0
  29. package/dist/core/plugins/resource-version-css.d.ts.map +1 -0
  30. package/dist/core/plugins/resource-version-css.js +41 -0
  31. package/dist/core/plugins/resource-version-css.js.map +1 -0
  32. package/dist/core/plugins/resource-version-html.d.ts +7 -0
  33. package/dist/core/plugins/resource-version-html.d.ts.map +1 -0
  34. package/dist/core/plugins/resource-version-html.js +66 -0
  35. package/dist/core/plugins/resource-version-html.js.map +1 -0
  36. package/dist/core/plugins/version-resources.d.ts +7 -0
  37. package/dist/core/plugins/version-resources.d.ts.map +1 -0
  38. package/dist/core/plugins/version-resources.js +89 -0
  39. package/dist/core/plugins/version-resources.js.map +1 -0
  40. package/docs/ai-context.md +15 -7
  41. package/docs/architecture.md +12 -7
  42. package/docs/default-ui-skins.md +4 -5
  43. package/docs/dev-resource-watching.md +23 -3
  44. package/docs/game-template.md +17 -0
  45. package/docs/release-assets.md +64 -55
  46. package/package.json +3 -2
  47. package/templates/empty/README.md +13 -1
  48. package/templates/empty/kurot.config.ts +1 -1
  49. package/templates/empty/src/Main.ts +5 -7
  50. package/templates/game/README.md +13 -1
  51. package/templates/game/kurot.config.ts +1 -1
  52. package/templates/game/src/Main.ts +3 -9
  53. package/templates/game/src/StyleManager.ts +3 -2
  54. package/dist/core/plugins/version-assets.d.ts +0 -7
  55. package/dist/core/plugins/version-assets.d.ts.map +0 -1
  56. package/dist/core/plugins/version-assets.js +0 -108
  57. package/dist/core/plugins/version-assets.js.map +0 -1
@@ -7,7 +7,7 @@ import { compileSource } from './compile-source.js';
7
7
  import { generateHtml } from './generate-html.js';
8
8
  import { writeManifest } from './manifest.js';
9
9
  import { copyAssets } from './copy-assets.js';
10
- import { versionAssets } from './version-assets.js';
10
+ import { versionResources } from './version-resources.js';
11
11
  export { cleanOutput } from './clean-output.js';
12
12
  export { compileKUI } from './compile-kui.js';
13
13
  export { compileEngine } from './compile-engine.js';
@@ -17,7 +17,7 @@ export { compileSource } from './compile-source.js';
17
17
  export { generateHtml } from './generate-html.js';
18
18
  export { writeManifest } from './manifest.js';
19
19
  export { copyAssets } from './copy-assets.js';
20
- export { versionAssets } from './version-assets.js';
20
+ export { versionResources } from './version-resources.js';
21
21
  /**
22
22
  * The standard build sequence.
23
23
  *
@@ -39,7 +39,7 @@ export function defaultPlugins() {
39
39
  generateHtml(),
40
40
  writeManifest(),
41
41
  copyAssets(),
42
- versionAssets(),
42
+ versionResources(),
43
43
  ];
44
44
  }
45
45
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/plugins/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO;QACN,WAAW,EAAE;QACb,UAAU,EAAE;QACZ,aAAa,EAAE;QACf,uBAAuB,EAAE;QACzB,qBAAqB,EAAE;QACvB,aAAa,EAAE;QACf,YAAY,EAAE;QACd,aAAa,EAAE;QACf,UAAU,EAAE;QACZ,aAAa,EAAE;KACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/plugins/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc;IAC7B,OAAO;QACN,WAAW,EAAE;QACb,UAAU,EAAE;QACZ,aAAa,EAAE;QACf,uBAAuB,EAAE;QACzB,qBAAqB,EAAE;QACvB,aAAa,EAAE;QACf,YAAY,EAAE;QACd,aAAa,EAAE;QACf,UAAU,EAAE;QACZ,gBAAgB,EAAE;KAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type ResourceUrlVersioner = (source: string, baseFile: string) => string;
2
+ /**
3
+ * Version CSS url() and string @import references, preserving comments and
4
+ * ordinary strings. CSS escaping is decoded for lookup and retained on output.
5
+ */
6
+ export declare function versionCssResources(css: string, file: string, versionUrl: ResourceUrlVersioner): string;
7
+ //# sourceMappingURL=resource-version-css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-version-css.d.ts","sourceRoot":"","sources":["../../../src/core/plugins/resource-version-css.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;AAEhF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,GAAG,MAAM,CAmCvG"}
@@ -0,0 +1,41 @@
1
+ import valueParser from 'postcss-value-parser';
2
+ /**
3
+ * Version CSS url() and string @import references, preserving comments and
4
+ * ordinary strings. CSS escaping is decoded for lookup and retained on output.
5
+ */
6
+ export function versionCssResources(css, file, versionUrl) {
7
+ const parsed = valueParser(css);
8
+ parsed.walk((node, index, nodes) => {
9
+ if (node.type === 'function' && node.value.toLowerCase() === 'url') {
10
+ const value = node.nodes.find(child => child.type !== 'space' && child.type !== 'comment');
11
+ if (value?.type === 'string' || value?.type === 'word') {
12
+ update(value);
13
+ }
14
+ return false;
15
+ }
16
+ if (node.type === 'word' && node.value.toLowerCase() === '@import') {
17
+ const next = nodes.slice(index + 1).find(child => child.type !== 'space' && child.type !== 'comment');
18
+ if (next?.type === 'string') {
19
+ update(next);
20
+ }
21
+ }
22
+ });
23
+ return parsed.toString();
24
+ function update(node) {
25
+ const decoded = node.value.replace(/\\(?:([a-f0-9]{1,6})\s?|([\s\S]))/gi, (_match, hex, character) => {
26
+ if (hex) {
27
+ const point = Number.parseInt(hex, 16);
28
+ return point > 0 && point <= 0x10ffff ? String.fromCodePoint(point) : '\uFFFD';
29
+ }
30
+ return character && !/[\r\n\f]/.test(character) ? character : '';
31
+ });
32
+ const next = versionUrl(decoded, file);
33
+ if (next === decoded) {
34
+ return;
35
+ }
36
+ node.value = node.type === 'word'
37
+ ? next.replace(/[\\\s"'()]/g, character => '\\' + character)
38
+ : next.replace(/\\/g, '\\\\').split(node.quote).join('\\' + node.quote);
39
+ }
40
+ }
41
+ //# sourceMappingURL=resource-version-css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-version-css.js","sourceRoot":"","sources":["../../../src/core/plugins/resource-version-css.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAI/C;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,IAAY,EAAE,UAAgC;IAC9F,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC;YACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;YAC3F,IAAI,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,KAAK,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxD,MAAM,CAAC,KAAK,CAAC,CAAC;YACf,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,SAAS,EAAE,CAAC;YACpE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;YACtG,IAAI,IAAI,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,CAAC;YACd,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;IAEzB,SAAS,MAAM,CAAC,IAAmD;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qCAAqC,EAAE,CAAC,MAAc,EAAE,GAAuB,EAAE,SAA6B,EAAE,EAAE;YACpJ,IAAI,GAAG,EAAE,CAAC;gBACT,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACvC,OAAO,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAChF,CAAC;YACD,OAAO,SAAS,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACtB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM;YAChC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,GAAG,SAAS,CAAC;YAC5D,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ResourceUrlVersioner } from './resource-version-css.js';
2
+ /**
3
+ * Write build metadata and static resource versions without serializing scripts
4
+ * or moving files. Navigation destinations and generated JS URLs remain intact.
5
+ */
6
+ export declare function versionHtmlResources(html: string, file: string, version: string, versionUrl: ResourceUrlVersioner): string;
7
+ //# sourceMappingURL=resource-version-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-version-html.d.ts","sourceRoot":"","sources":["../../../src/core/plugins/resource-version-html.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAQtE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,GAAG,MAAM,CAsD1H"}
@@ -0,0 +1,66 @@
1
+ import { defaultTreeAdapter, parse } from 'parse5';
2
+ import { BuildError } from '../errors.js';
3
+ import { versionCssResources } from './resource-version-css.js';
4
+ /**
5
+ * Write build metadata and static resource versions without serializing scripts
6
+ * or moving files. Navigation destinations and generated JS URLs remain intact.
7
+ */
8
+ export function versionHtmlResources(html, file, version, versionUrl) {
9
+ const document = parse(html, { sourceCodeLocationInfo: true });
10
+ const pending = [document];
11
+ const edits = [];
12
+ let headEnd;
13
+ while (pending.length > 0) {
14
+ const parent = pending.pop();
15
+ for (const element of parent.childNodes.filter(defaultTreeAdapter.isElementNode)) {
16
+ pending.push(element);
17
+ if (element.tagName === 'base') {
18
+ throw new BuildError('output.versionedAssets requires document-relative resources; remove the authored base element.');
19
+ }
20
+ if (element.tagName === 'head') {
21
+ headEnd = element.sourceCodeLocation?.startTag?.endOffset;
22
+ }
23
+ if (element.tagName === 'meta' && element.attrs.some(attr => attr.name === 'name' && attr.value === 'kurot-resource-version')) {
24
+ throw new BuildError('output.versionedAssets owns the kurot-resource-version metadata; remove the authored value.');
25
+ }
26
+ for (const attr of element.attrs) {
27
+ const attributeName = attr.prefix ? `${attr.prefix}:${attr.name}` : attr.name;
28
+ const location = element.sourceCodeLocation?.attrs?.[attributeName];
29
+ if (!location) {
30
+ continue;
31
+ }
32
+ const asset = attr.name === 'src' || attr.name === 'poster' ||
33
+ (attr.name === 'href' && ['link', 'image', 'use'].includes(element.tagName)) ||
34
+ (attr.name === 'data' && element.tagName === 'object');
35
+ const next = attr.name === 'style'
36
+ ? versionCssResources(attr.value, file, versionUrl)
37
+ : asset ? versionUrl(attr.value, file) : attr.value;
38
+ if (next !== attr.value) {
39
+ edits.push({ start: location.startOffset, end: location.endOffset, text: `${attributeName}="${escapeAttribute(next)}"` });
40
+ }
41
+ }
42
+ if (element.tagName === 'style') {
43
+ for (const text of element.childNodes.filter(defaultTreeAdapter.isTextNode)) {
44
+ const location = text.sourceCodeLocation;
45
+ const next = versionCssResources(text.value, file, versionUrl);
46
+ if (location && next !== text.value) {
47
+ edits.push({ start: location.startOffset, end: location.endOffset, text: next });
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ if (headEnd === undefined) {
54
+ throw new BuildError('output.versionedAssets requires an explicit HTML head element.');
55
+ }
56
+ edits.push({ start: headEnd, end: headEnd, text: `\n\t<meta name="kurot-resource-version" content="${escapeAttribute(version)}">` });
57
+ let result = html;
58
+ for (const edit of edits.sort((left, right) => right.start - left.start)) {
59
+ result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
60
+ }
61
+ return result;
62
+ }
63
+ function escapeAttribute(value) {
64
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
65
+ }
66
+ //# sourceMappingURL=resource-version-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-version-html.js","sourceRoot":"","sources":["../../../src/core/plugins/resource-version-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { BuildPlugin } from '../pipeline.js';
2
+ /**
3
+ * Stamp resource requests with the release build number. Generated JavaScript
4
+ * keeps its content-hashed names and original paths; no document base is changed.
5
+ */
6
+ export declare function versionResources(): BuildPlugin;
7
+ //# sourceMappingURL=version-resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-resources.d.ts","sourceRoot":"","sources":["../../../src/core/plugins/version-resources.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKhE;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CA2B9C"}
@@ -0,0 +1,89 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { BuildError } from '../errors.js';
4
+ import { versionCssResources } from './resource-version-css.js';
5
+ import { versionHtmlResources } from './resource-version-html.js';
6
+ /**
7
+ * Stamp resource requests with the release build number. Generated JavaScript
8
+ * keeps its content-hashed names and original paths; no document base is changed.
9
+ */
10
+ export function versionResources() {
11
+ return {
12
+ name: 'version release resources',
13
+ async apply(ctx) {
14
+ if (ctx.project.mode !== 'release' || !ctx.project.config.output.versionedAssets) {
15
+ return;
16
+ }
17
+ const files = await listFiles(ctx.project.outputDir);
18
+ const version = path.basename(ctx.project.outputDir);
19
+ const versionUrl = createVersioner(new Set(files), version);
20
+ const updates = [];
21
+ for (const file of files) {
22
+ if (!/\.(?:html|css)$/i.test(file)) {
23
+ continue;
24
+ }
25
+ const source = await fs.readFile(path.join(ctx.project.outputDir, file), 'utf8');
26
+ const contents = /\.html$/i.test(file)
27
+ ? versionHtmlResources(source, file, version, versionUrl)
28
+ : versionCssResources(source, file, versionUrl);
29
+ updates.push({ file, contents });
30
+ }
31
+ // Validate every page before replacing any generated HTML/CSS.
32
+ for (const update of updates) {
33
+ await fs.writeFile(path.join(ctx.project.outputDir, update.file), update.contents);
34
+ }
35
+ },
36
+ };
37
+ }
38
+ function createVersioner(files, version) {
39
+ return (source, baseFile) => {
40
+ if (!source || source.startsWith('#')) {
41
+ return source;
42
+ }
43
+ let target;
44
+ try {
45
+ target = new URL(source, `https://kurot.invalid/${baseFile}`);
46
+ }
47
+ catch {
48
+ return source;
49
+ }
50
+ if (target.origin !== 'https://kurot.invalid') {
51
+ return source;
52
+ }
53
+ let file;
54
+ try {
55
+ file = decodeURIComponent(target.pathname).slice(1);
56
+ }
57
+ catch {
58
+ return source;
59
+ }
60
+ if (!files.has(file) || file.startsWith('js/') || /\.html?$/i.test(file)) {
61
+ return source;
62
+ }
63
+ const hash = source.indexOf('#');
64
+ const fragment = hash < 0 ? '' : source.slice(hash);
65
+ const reference = hash < 0 ? source : source.slice(0, hash);
66
+ const query = reference.indexOf('?');
67
+ const pathname = query < 0 ? reference : reference.slice(0, query);
68
+ const parameters = new URLSearchParams(query < 0 ? '' : reference.slice(query + 1));
69
+ parameters.set('v', version);
70
+ return `${pathname}?${parameters}${fragment}`;
71
+ };
72
+ }
73
+ async function listFiles(root, directory = '') {
74
+ const files = [];
75
+ for (const entry of await fs.readdir(path.join(root, directory), { withFileTypes: true })) {
76
+ const relative = path.posix.join(directory, entry.name);
77
+ if (entry.isDirectory()) {
78
+ files.push(...await listFiles(root, relative));
79
+ }
80
+ else if (entry.isFile()) {
81
+ files.push(relative);
82
+ }
83
+ else {
84
+ throw new BuildError(`Versioned resources require regular output files: ${relative}`);
85
+ }
86
+ }
87
+ return files;
88
+ }
89
+ //# sourceMappingURL=version-resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-resources.js","sourceRoot":"","sources":["../../../src/core/plugins/version-resources.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  # @kurot/cli — AI context map
2
2
 
3
- `@kurot/cli@3.7.0` is build-time tooling on Node.js >= 20, with ordinary
3
+ `@kurot/cli@3.8.1` (prepared for publication) is build-time tooling on Node.js >= 20, with ordinary
4
4
  ui-document dependency **^0.13.0**. Projects supply their own Core/UI runtime.
5
5
  Read [architecture](architecture.md) for the plugin pipeline; the source and
6
6
  configuration exports establish current behavior.
@@ -150,12 +150,20 @@ manual prefix cannot conflict with `ui.components.namespace`.
150
150
  Do not reorder namespace or source compilation without reviewing externalization
151
151
  in `namespace-external-plugin.ts`.
152
152
 
153
- Release versioning runs last so the digest covers generated themes, scripts,
154
- resource bytes and template assets. It keeps index.html/manifest.json at the
155
- output root and moves all other files under assets/<hash>/. The HTML base URL
156
- versions document-relative requests; callers that explicitly resolve URLs must
157
- use document.baseURI. Existing UI Theme must receive an absolute config URL.
158
- See [release assets](release-assets.md). This option requires CLI >= 3.7.0 within 3.x.
153
+ CLI >= 3.8.1 within 3.x with Core >= 2.7.0 within 2.x supports resource
154
+ queries. `version-resources.ts` runs last and
155
+ stamps each HTML page with the release directory's timestamp. HTML/CSS local
156
+ assets receive a `v` query; Core transports version local resource/ requests.
157
+ Generated JS, import maps, manifest paths and output directories stay unchanged.
158
+ `resource-version-html.ts` preserves script bytes and owns metadata validation;
159
+ `resource-version-css.ts` owns CSS URL parsing. Native fetch/FontFace/Theme calls
160
+ use Core's resolveResourceUrl; new starters enable the flag explicitly. See
161
+ [release resources](release-assets.md) for the protocol and deployment limits.
162
+
163
+ Since CLI 3.8.0, root ESM config stage changes regenerate HTML in `kurot dev` without
164
+ restarting dev; `core/config-watcher.ts` owns this path. See
165
+ [development watching](dev-resource-watching.md). Core canvas-attribute startup
166
+ requires Core >= 2.6.0 within 2.x; explicit overrides remain supported.
159
167
 
160
168
  ## Configuration lookup
161
169
 
@@ -40,7 +40,7 @@ factory variables, property assignments, semantic layout children, and state
40
40
  property overrides. Document contracts, reusable instances, data bindings, and
41
41
  actions remain `UIDocument` concepts.
42
42
 
43
- CLI 3.7.0 consumes ui-document ^0.13.0, retaining Label text-fitting metadata. The
43
+ CLI 3.8.1 consumes ui-document ^0.13.0, retaining Label text-fitting metadata. The
44
44
  compiler emits `textFit`, `minFontSize`, base font sizes and state overrides;
45
45
  native UI owns measurement and derives drawing sizes after layout validation.
46
46
  See [Label compilation](label-text-layout.md). The application supplies UI
@@ -72,12 +72,13 @@ Development builds preserve application source module structure under
72
72
  `manifest.json`. Static resources retain stable paths because application code
73
73
  addresses them by key or URL.
74
74
 
75
- With `output.versionedAssets: true`, the final stage hashes the complete output
76
- and moves runtime files into `assets/<hash>/`. The entry HTML receives a base URL;
77
- the root release manifest and BuildContext script paths receive the same prefix.
78
- Relative references inside resources and ESM modules remain intact. This is one
79
- version for the complete build, rather than independent hashes per resource.
80
- See [release deployment](release-assets.md) for URL and hosting requirements.
75
+ With CLI >= 3.8.1 within 3.x and Core >= 2.7.0 within 2.x,
76
+ `output.versionedAssets: true` writes the release directory timestamp to HTML
77
+ metadata and appends it as `v` to static HTML/CSS assets. Core transports use that
78
+ metadata for local resource/ files, including secondary atlas/font pages. Native
79
+ fetch/FontFace/Theme callers use Core's resolveResourceUrl. Generated JS hashes,
80
+ manifest paths and BuildContext outputs remain unchanged. No files move and no
81
+ HTML base is inserted. See [release resources](release-assets.md).
81
82
 
82
83
  Asset copying creates directories only when an accepted runtime file needs them.
83
84
  KUI source files and source theme JSON are excluded when UI compilation is enabled,
@@ -97,6 +98,10 @@ and reconciles additions, deletions and directory replacement. Runtime-only
97
98
  changes do not recompile skins. The fixed style.json and default.res.json paths
98
99
  still trigger KUI compilation; font loading remains an application responsibility.
99
100
  Changes are debounced and queued, with changed bytes staged before publication.
101
+ A shallow root configuration watcher re-evaluates ESM config after stage edits
102
+ and publishes complete HTML through an atomic rename. Invalid values retain the
103
+ last successful page; structural build changes require restarting dev. This path
104
+ shares the resource build queue and does not mutate the immutable Project.
100
105
  Browser refresh remains manual and HTTP responses use no-store. Individual
101
106
  renames do not form an atomic multi-file transaction. See
102
107
  [resource watching](dev-resource-watching.md).
@@ -81,19 +81,18 @@ and wrap the node with Group/Image/Scroller for its surface and viewport.
81
81
  `src/DefaultUI.ts` only connects data and events. The example installs its own
82
82
  project font loader and document dependency; other projects are unchanged.
83
83
 
84
- Use the checkout CLI with the example's installed SDKs. From the repository root:
84
+ Use the example's installed registry CLI and SDKs. From the repository root:
85
85
 
86
86
  ```sh
87
- pnpm --dir packages/cli build
88
- cd examples/game
89
- node ../../packages/cli/dist/index.js dev
87
+ pnpm --dir examples/game install --frozen-lockfile
88
+ pnpm --dir examples/game dev
90
89
  ```
91
90
 
92
91
  Open the server URL with `?ui=1`; the ordinary scene remains available without
93
92
  that parameter. Try the inputs, switches, ComboBox, tabs, sliders, panel close
94
93
  button and draggable List. The gallery uses the example's installed Core/UI
95
94
  with compiled KUI XML, without modifying dependencies or locks. In that example
96
- directory, the same CLI accepts `build` and `build --release` for one-shot builds.
95
+ directory, `pnpm build --strict` and `pnpm release --strict` run one-shot builds.
97
96
  The private asset tool's isolated source preview remains an optional maintenance
98
97
  tool for testing checkout engine changes; it is not required by the installed Core.
99
98
 
@@ -17,9 +17,26 @@ translations and other project-owned files.
17
17
  | PNG, fonts, language properties and other runtime files | Synchronize assets |
18
18
  | Resource directory creation, replacement or deletion | Reconnect watcher and reconcile output; compile KUI when enabled |
19
19
  | Reusable component source or skin | Retain component discovery/namespace/catalog rebuild and synchronize assets |
20
-
21
- Changes to `kurot.config.ts`, dependencies or build configuration still require
22
- restarting dev. Locale configuration is consumed by project code; the CLI does
20
+ | Root kurot.config.ts or .js stage defaults | Validate and atomically regenerate HTML; refresh manually |
21
+
22
+ With CLI >= 3.8.0 within 3.x, stage defaults in `kurot.config.ts` / `.js` can be
23
+ saved without restarting `kurot dev`. `kurot build --watch` retains its existing
24
+ source-watch behavior and does not install the configuration watcher.
25
+ After the successful configuration update, refresh the browser manually. The
26
+ new Player consumes HTML canvas attributes with the Core behavior described in
27
+ [stage configuration](../../core/docs/stage-configuration.md), requiring Core
28
+ >= 2.6.0 within 2.x; explicit runtime options still take priority.
29
+
30
+ The watcher handles ordinary and atomic editor saves of the root ESM config file,
31
+ re-evaluates it with a fresh module URL, validates width/height, scale mode,
32
+ orientation and frame rate, and atomically replaces only the generated HTML.
33
+ Invalid edits keep the last successful page, report an error and allow correction
34
+ without restarting. Imported configuration helpers and dependencies are not
35
+ reloaded; edits to them still require restarting dev.
36
+
37
+ Changes outside `stage`, including entry/output/UI paths, or dependencies still
38
+ require restarting dev. A detected structural config change reports
39
+ `KUROT_CONFIG_RESTART_REQUIRED` and leaves the active outputs intact. Locale configuration is consumed by project code; the CLI does
23
40
  not introduce a locale parser or mutate authored skins. A style edit may change
24
41
  compiled skin assignments while its XML remains untouched.
25
42
 
@@ -52,6 +69,9 @@ A resource-only batch emits JSONL build-start with reason `resource-change`,
52
69
  followed by build-complete after synchronization. Existing `kui-change` and
53
70
  `source-change` reasons retain their meaning. Consumers should tolerate the
54
71
  additional reason and refresh only after a successful build-complete event.
72
+ Root configuration updates emit `config-change`; failures additionally emit
73
+ `KUROT_CONFIG_RELOAD_FAILED` or `KUROT_CONFIG_RESTART_REQUIRED` diagnostics.
74
+ The stage watcher shares this serialized queue with resource/component work.
55
75
 
56
76
  ## Preview and atlas boundaries
57
77
 
@@ -31,6 +31,23 @@ This ordering includes loading-screen Labels in the project font. The loading
31
31
  screen's startup messages stay in English because translations are not loaded yet.
32
32
  Templates do not implement live font/theme/language replacement.
33
33
 
34
+ ## Stage defaults
35
+
36
+ Both starters use `createPlayer({ canvas })`. Configure width, height, scale mode,
37
+ orientation and frame rate in `kurot.config.ts`; the CLI writes canvas attributes.
38
+ This requires Core >= 2.6.0 within 2.x, documented in
39
+ [stage configuration](../../core/docs/stage-configuration.md). Explicit options
40
+ remain available for intentional overrides. In dev, save stage settings, wait
41
+ for the configuration update and refresh the browser with CLI >= 3.8.0 within
42
+ 3.x; no dev restart is needed.
43
+
44
+ ## Release resources
45
+
46
+ Both starters enable output.versionedAssets. Resource queries use the release
47
+ build number with Core >= 2.7.0 within 2.x and CLI >= 3.8.1 within 3.x. The game
48
+ starter resolves native style/font and Theme requests through resolveResourceUrl
49
+ before Player creation. See [release resources](release-assets.md).
50
+
34
51
  ## HTML brand splash
35
52
 
36
53
  The game template's `web/index.html` presents `web/logo.png` beneath a small
@@ -1,76 +1,85 @@
1
- # Versioned release assets
1
+ # Release resource versions
2
2
 
3
- Enable content-addressed release packaging in `kurot.config.ts`:
3
+ The query-based protocol requires **CLI >= 3.8.1 within 3.x and Core >= 2.7.0
4
+ within 2.x**. These source versions are prepared for publication. It replaces
5
+ the earlier whole-build directory packaging.
6
+
7
+ Enable it in `kurot.config.ts`:
4
8
 
5
9
  ```ts
6
10
  export default {
7
- output: { dir: 'bin-debug', versionedAssets: true },
8
- // Keep the project's other settings.
11
+ output: { dir: 'bin-debug', versionedAssets: true },
12
+ // Keep the project's other settings.
9
13
  };
10
14
  ```
11
15
 
12
- The option requires CLI >= 3.7.0 within 3.x. It defaults to false and is ignored
13
- by development/watch builds.
14
- An index query string does not propagate to requests for resources.
16
+ The configuration field defaults to false for existing configurations. New game
17
+ and empty starters explicitly enable it. Development/watch builds are unchanged.
15
18
 
16
- ## Output and URLs
19
+ ## Output and build number
17
20
 
18
21
  ```text
19
- bin-release/web/<timestamp>/
22
+ bin-release/web/261030123456/
20
23
  ├── index.html
21
24
  ├── manifest.json
22
- └── assets/<hash>/
23
- ├── js/
24
- ├── resource/
25
- └── <files copied beside the HTML template>
25
+ ├── js/main.min_<content-hash>.js
26
+ ├── resource/
27
+ └── <files copied beside the HTML template>
26
28
  ```
27
29
 
28
- The hash is the first 32 hexadecimal characters of SHA-256 over sorted output
29
- paths, file sizes and bytes, including generated HTML, theme JSON and scripts.
30
- Any changed file changes the complete build's version. Identical builds retain
31
- their URL; unchanged individual files also get new URLs when another file changes.
32
- This trades granular cache reuse for one consistent release across all loaders.
33
-
34
- The CLI inserts `<base href="./assets/<hash>/">` first inside `<head>`. A request
35
- for `resource/assets/atlas.png` then resolves under the version directory, even
36
- when the entry is `/game/index.html?launch=...`. Atlas `file` references, bitmap
37
- font pages, stylesheet fonts, relative imports and direct browser requests use
38
- that directory without rewriting authored resources or keys. The release manifest
39
- stays at the root with prefixed script paths. Source maps move with their scripts.
40
-
41
- Custom templates must have an explicit `<head>` and no `<base>` element. The
42
- CLI inserts the base at the parsed start tag's source location without
43
- reserializing the template. Quoted attributes and script/style text are preserved.
44
- The build rejects symbolic links and other nonregular output files. Relative URLs
45
- must refer to files inside the build; root-relative URLs, remote URLs, and URLs
46
- explicitly based on location.href remain outside this mechanism. Applications
47
- that construct URLs should use document.baseURI for build assets. Relative links
48
- and forms also inherit the base; use explicit destinations for application navigation.
49
- If hosting has a CSP base-uri directive, it must allow this same-origin base.
50
-
51
- ## Theme loading with existing UI
52
-
53
- UI runtimes that resolve a relative Theme config against location.href need an
54
- absolute config URL. The game starter uses:
30
+ The existing release directory timestamp (`YYMMDDHHmmss`) is the resource build
31
+ number. The CLI inserts `<meta name="kurot-resource-version" content="261030123456">`
32
+ into each generated HTML page. It creates no asset hash directory or HTML base.
33
+ The release manifest, import map, generated JS filenames, source maps and JS
34
+ imports retain their original paths. Identical JS can retain its filename hash
35
+ across releases even when the resource build number changes.
36
+
37
+ Core reads the metadata before Player creation. Its GET HttpRequest, ImageLoader,
38
+ Sound, Video and sound-resource transports append `v` to same-origin files below
39
+ the page's `resource/` directory. Resource manifests, atlas and bitmap-font pages,
40
+ and adapters using these transports receive the same version. Authored resource
41
+ keys and descriptors remain unchanged. Existing query fields/fragments survive;
42
+ an existing `v` is replaced once.
43
+
44
+ Native browser callers must opt in through the public Core helper:
55
45
 
56
46
  ```ts
57
- const theme = new Theme(new URL('resource/default.thm.json', document.baseURI).href);
47
+ import { resolveResourceUrl } from '@kurot/core';
48
+
49
+ const response = await fetch(resolveResourceUrl('resource/config/style.json'));
50
+ const font = new FontFace('ProjectFont', `url("${resolveResourceUrl('resource/font.ttf')}")`);
51
+ const theme = new Theme(resolveResourceUrl(new URL('resource/default.thm.json', document.baseURI)));
58
52
  ```
59
53
 
60
- Use the same pattern in existing projects. This keeps the theme's `skinsJs`
61
- import inside the version directory without requiring a UI runtime update.
54
+ The game starter includes this wiring for startup fonts/styles and Theme. Theme
55
+ config is versioned, while its generated `skinsJs` continues using the JS filename
56
+ hash. Core does not replace global fetch or XMLHttpRequest. API requests outside
57
+ `resource/`, POST requests, remote/CDN URLs, data/blob URLs and JS modules retain
58
+ their original URLs. Remote or signed resources need their own version policy.
59
+
60
+ ## Static HTML and CSS
61
+
62
+ The CLI also versions existing local files referenced by HTML `src`, `poster`,
63
+ link/SVG `href`, object `data`, inline styles, style blocks, CSS `url()` and string
64
+ `@import`. This includes page logos, fonts, stylesheets and startup configuration
65
+ scripts outside `js/`. It preserves scripts and ordinary strings. Navigation
66
+ links, form destinations, HTML pages and generated files under `js/` are unchanged.
67
+ Native URLs constructed inside scripts must use the helper or read the generated
68
+ metadata in a standalone page without Core. `srcset` rewriting is not provided.
62
69
 
63
- ## Deployment
70
+ Custom HTML pages need an explicit `<head>` and no authored `<base>` or
71
+ `kurot-resource-version` metadata. The build validates every page before rewriting
72
+ HTML/CSS. Symbolic links and other nonregular output files are rejected.
73
+ Use document-relative paths to support deployment under a subdirectory.
64
74
 
65
- Upload the complete generated directory, including `assets/<hash>/`. Upload new
66
- asset directories first, then replace root manifest.json and index.html. Keep
67
- previous version directories available while older sessions may request assets;
68
- avoid deploy commands that delete those directories immediately. Publish the
69
- entry at a directory URL ending in `/` or at its actual `index.html` URL.
75
+ ## Deployment and caching
70
76
 
71
- Serve index.html with revalidation, for example `Cache-Control: no-cache`.
72
- Serve `assets/<hash>/**` with `Cache-Control: public, max-age=31536000, immutable`.
73
- If a launcher reads root manifest.json, give it revalidation too. These are server
74
- settings; the CLI writes static files and cannot set response headers. Never
75
- overwrite files inside an already published hash directory. Rollback replaces
76
- the root entry files with those of the previous complete release.
77
+ Deploy the entire generated release directory. Revalidate entry HTML and
78
+ manifest.json (for example `Cache-Control: no-cache`). Configure CDN/cache keys to
79
+ include `v`; otherwise different builds can still share stale resource responses.
80
+ Generated content-hashed JS can retain long-lived immutable caching. Resource
81
+ URLs may be cached by their version query, but replacing a same-path file does
82
+ not retain older bytes for an uncached old URL. This is cache busting, not release
83
+ snapshot storage. Use separate deployment directories if old sessions must lazily
84
+ load their exact previous files after deployment. Server headers and atomic
85
+ publishing remain deployment responsibilities.