@hozu/validator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/context.d.ts +13 -0
  4. package/dist/context.d.ts.map +1 -0
  5. package/dist/context.js +27 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/contracts/lock.d.ts +19 -0
  8. package/dist/contracts/lock.d.ts.map +1 -0
  9. package/dist/contracts/lock.js +61 -0
  10. package/dist/contracts/lock.js.map +1 -0
  11. package/dist/contracts/run.d.ts +14 -0
  12. package/dist/contracts/run.d.ts.map +1 -0
  13. package/dist/contracts/run.js +130 -0
  14. package/dist/contracts/run.js.map +1 -0
  15. package/dist/contracts/skeleton.d.ts +4 -0
  16. package/dist/contracts/skeleton.d.ts.map +1 -0
  17. package/dist/contracts/skeleton.js +108 -0
  18. package/dist/contracts/skeleton.js.map +1 -0
  19. package/dist/contracts/verify.d.ts +5 -0
  20. package/dist/contracts/verify.d.ts.map +1 -0
  21. package/dist/contracts/verify.js +58 -0
  22. package/dist/contracts/verify.js.map +1 -0
  23. package/dist/env.d.ts +40 -0
  24. package/dist/env.d.ts.map +1 -0
  25. package/dist/env.js +79 -0
  26. package/dist/env.js.map +1 -0
  27. package/dist/impact.d.ts +27 -0
  28. package/dist/impact.d.ts.map +1 -0
  29. package/dist/impact.js +154 -0
  30. package/dist/impact.js.map +1 -0
  31. package/dist/index.d.ts +22 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +71 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/resolve.d.ts +13 -0
  36. package/dist/resolve.d.ts.map +1 -0
  37. package/dist/resolve.js +58 -0
  38. package/dist/resolve.js.map +1 -0
  39. package/dist/rules/classes.d.ts +3 -0
  40. package/dist/rules/classes.d.ts.map +1 -0
  41. package/dist/rules/classes.js +60 -0
  42. package/dist/rules/classes.js.map +1 -0
  43. package/dist/rules/data.d.ts +4 -0
  44. package/dist/rules/data.d.ts.map +1 -0
  45. package/dist/rules/data.js +34 -0
  46. package/dist/rules/data.js.map +1 -0
  47. package/dist/rules/dom.d.ts +3 -0
  48. package/dist/rules/dom.d.ts.map +1 -0
  49. package/dist/rules/dom.js +104 -0
  50. package/dist/rules/dom.js.map +1 -0
  51. package/dist/rules/errors.d.ts +3 -0
  52. package/dist/rules/errors.d.ts.map +1 -0
  53. package/dist/rules/errors.js +97 -0
  54. package/dist/rules/errors.js.map +1 -0
  55. package/dist/rules/events.d.ts +5 -0
  56. package/dist/rules/events.d.ts.map +1 -0
  57. package/dist/rules/events.js +87 -0
  58. package/dist/rules/events.js.map +1 -0
  59. package/dist/rules/forms.d.ts +3 -0
  60. package/dist/rules/forms.d.ts.map +1 -0
  61. package/dist/rules/forms.js +19 -0
  62. package/dist/rules/forms.js.map +1 -0
  63. package/dist/rules/html.d.ts +3 -0
  64. package/dist/rules/html.d.ts.map +1 -0
  65. package/dist/rules/html.js +55 -0
  66. package/dist/rules/html.js.map +1 -0
  67. package/dist/rules/http.d.ts +3 -0
  68. package/dist/rules/http.d.ts.map +1 -0
  69. package/dist/rules/http.js +71 -0
  70. package/dist/rules/http.js.map +1 -0
  71. package/dist/rules/i18n.d.ts +3 -0
  72. package/dist/rules/i18n.d.ts.map +1 -0
  73. package/dist/rules/i18n.js +201 -0
  74. package/dist/rules/i18n.js.map +1 -0
  75. package/dist/rules/images.d.ts +3 -0
  76. package/dist/rules/images.d.ts.map +1 -0
  77. package/dist/rules/images.js +32 -0
  78. package/dist/rules/images.js.map +1 -0
  79. package/dist/rules/links.d.ts +3 -0
  80. package/dist/rules/links.d.ts.map +1 -0
  81. package/dist/rules/links.js +60 -0
  82. package/dist/rules/links.js.map +1 -0
  83. package/dist/rules/literals.d.ts +3 -0
  84. package/dist/rules/literals.d.ts.map +1 -0
  85. package/dist/rules/literals.js +147 -0
  86. package/dist/rules/literals.js.map +1 -0
  87. package/dist/rules/paths.d.ts +3 -0
  88. package/dist/rules/paths.d.ts.map +1 -0
  89. package/dist/rules/paths.js +177 -0
  90. package/dist/rules/paths.js.map +1 -0
  91. package/dist/rules/pwa.d.ts +3 -0
  92. package/dist/rules/pwa.d.ts.map +1 -0
  93. package/dist/rules/pwa.js +28 -0
  94. package/dist/rules/pwa.js.map +1 -0
  95. package/dist/rules/refs.d.ts +5 -0
  96. package/dist/rules/refs.d.ts.map +1 -0
  97. package/dist/rules/refs.js +128 -0
  98. package/dist/rules/refs.js.map +1 -0
  99. package/dist/rules/rendering.d.ts +3 -0
  100. package/dist/rules/rendering.d.ts.map +1 -0
  101. package/dist/rules/rendering.js +37 -0
  102. package/dist/rules/rendering.js.map +1 -0
  103. package/dist/rules/routes.d.ts +4 -0
  104. package/dist/rules/routes.d.ts.map +1 -0
  105. package/dist/rules/routes.js +108 -0
  106. package/dist/rules/routes.js.map +1 -0
  107. package/dist/rules/states.d.ts +6 -0
  108. package/dist/rules/states.d.ts.map +1 -0
  109. package/dist/rules/states.js +167 -0
  110. package/dist/rules/states.js.map +1 -0
  111. package/dist/rules/text.d.ts +3 -0
  112. package/dist/rules/text.d.ts.map +1 -0
  113. package/dist/rules/text.js +148 -0
  114. package/dist/rules/text.js.map +1 -0
  115. package/dist/rules/widgets.d.ts +3 -0
  116. package/dist/rules/widgets.d.ts.map +1 -0
  117. package/dist/rules/widgets.js +33 -0
  118. package/dist/rules/widgets.js.map +1 -0
  119. package/dist/schema.d.ts +19 -0
  120. package/dist/schema.d.ts.map +1 -0
  121. package/dist/schema.js +142 -0
  122. package/dist/schema.js.map +1 -0
  123. package/dist/sites.d.ts +15 -0
  124. package/dist/sites.d.ts.map +1 -0
  125. package/dist/sites.js +151 -0
  126. package/dist/sites.js.map +1 -0
  127. package/dist/suggest.d.ts +4 -0
  128. package/dist/suggest.d.ts.map +1 -0
  129. package/dist/suggest.js +30 -0
  130. package/dist/suggest.js.map +1 -0
  131. package/dist/walk.d.ts +37 -0
  132. package/dist/walk.d.ts.map +1 -0
  133. package/dist/walk.js +138 -0
  134. package/dist/walk.js.map +1 -0
  135. package/package.json +46 -0
@@ -0,0 +1,104 @@
1
+ import { at, eventFields, resolveAt, } from '@hozu/core/ir';
2
+ import { closest, didYouMean } from '../suggest.js';
3
+ import { walkView } from '../walk.js';
4
+ function domRefs(v, pointer, visit) {
5
+ if ('ref' in v) {
6
+ if (v.ref === 'dom')
7
+ visit(v.path[0] ?? '', at(pointer, 'path', 0));
8
+ }
9
+ else if ('object' in v)
10
+ for (const k in v.object)
11
+ domRefs(v.object[k], at(pointer, 'object', k), visit);
12
+ else if ('fn' in v)
13
+ domRefs(v.arg, at(pointer, 'arg'), visit);
14
+ else if ('test' in v)
15
+ guardDomRefs(v.test, at(pointer, 'test'), visit);
16
+ else if ('link' in v) {
17
+ domRefs(v.params, at(pointer, 'params'), visit);
18
+ domRefs(v.search, at(pointer, 'search'), visit);
19
+ }
20
+ }
21
+ function guardDomRefs(g, pointer, visit) {
22
+ switch (g.op) {
23
+ case 'and':
24
+ case 'or':
25
+ g.args.forEach((a, i) => guardDomRefs(a, at(pointer, 'args', i), visit));
26
+ return;
27
+ case 'not':
28
+ guardDomRefs(g.arg, at(pointer, 'arg'), visit);
29
+ return;
30
+ case 'fn':
31
+ domRefs(g.arg, at(pointer, 'arg'), visit);
32
+ return;
33
+ default:
34
+ domRefs(g.left, at(pointer, 'left'), visit);
35
+ domRefs(g.right, at(pointer, 'right'), visit);
36
+ }
37
+ }
38
+ export function domFields(ctx) {
39
+ const { ir } = ctx;
40
+ for (const f of Object.values(ir.features))
41
+ for (const [vid, view] of Object.entries(f.views))
42
+ walkView(ir, f, vid, view, ({ node, pointer }) => {
43
+ const outside = (field, p) => ctx.report('HZ027', f.id, p, `ui.dom.${field} is read outside an event handler`, 'DOM fields only exist while an event is being handled: read them in ui.send(…) payloads.', {
44
+ summary: 'Send the DOM value into the machine and read it from context',
45
+ snippet: null,
46
+ patch: null,
47
+ });
48
+ const fieldsFor = (on, pointer) => {
49
+ for (const [event, send] of Object.entries(on)) {
50
+ const allowed = eventFields[event] ?? [];
51
+ domRefs(send.payload, at(pointer, 'on', event, 'payload'), (field, p) => {
52
+ if (allowed.includes(field))
53
+ return;
54
+ const guess = closest(field, allowed);
55
+ ctx.report('HZ027', f.id, p, `"${event}" events have no DOM field "${field}".${didYouMean(guess)}`, allowed.length
56
+ ? `Fields available on ${event}: ${allowed.join(', ')}.`
57
+ : `${event} events carry no DOM fields.`, {
58
+ summary: guess ? `Read ui.dom.${guess}` : 'Remove the DOM field from the payload',
59
+ snippet: null,
60
+ patch: guess ? [{ op: 'replace', path: resolveAt(p), value: guess }] : null,
61
+ });
62
+ });
63
+ }
64
+ };
65
+ switch (node.kind) {
66
+ case 'widget':
67
+ domRefs(node.props, at(pointer, 'props'), outside);
68
+ for (const key of ['toggle', 'vars'])
69
+ for (const [name, v] of Object.entries(node[key]))
70
+ domRefs(v, at(pointer, key, name), outside);
71
+ for (const [event, send] of Object.entries(node.on))
72
+ domRefs(send.payload, at(pointer, 'on', event, 'payload'), (field, p) => {
73
+ if (field !== 'detail')
74
+ ctx.report('HZ027', f.id, p, `Widget event "${event}" carries only its detail`, 'Read the widget event payload through the handler argument: (detail) => ui.send(…, { x: detail.x }).');
75
+ });
76
+ return;
77
+ case 'el':
78
+ for (const key of ['attrs', 'toggle', 'vars'])
79
+ for (const [name, v] of Object.entries(node[key]))
80
+ domRefs(v, at(pointer, key, name), outside);
81
+ fieldsFor(node.on, pointer);
82
+ return;
83
+ case 'text':
84
+ case 'html':
85
+ domRefs(node.value, at(pointer, 'value'), outside);
86
+ return;
87
+ case 'if':
88
+ guardDomRefs(node.test, at(pointer, 'test'), outside);
89
+ return;
90
+ case 'global':
91
+ fieldsFor(node.on, pointer);
92
+ return;
93
+ case 'each':
94
+ domRefs(node.source, at(pointer, 'source'), outside);
95
+ return;
96
+ case 'query':
97
+ domRefs(node.input, at(pointer, 'input'), outside);
98
+ return;
99
+ default:
100
+ return;
101
+ }
102
+ });
103
+ }
104
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/rules/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,EAAE,EACF,WAAW,EAEX,SAAS,GAGV,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAIrC,SAAS,OAAO,CAAC,CAAY,EAAE,OAAW,EAAE,KAAY;IACtD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK;YAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;IACrE,CAAC;SAAM,IAAI,QAAQ,IAAI,CAAC;QAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;SACrG,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;SACxD,IAAI,MAAM,IAAI,CAAC;QAAE,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAA;SACjE,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAA;QAC/C,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAA;IACjD,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,CAAY,EAAE,OAAW,EAAE,KAAY;IAC3D,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;QACb,KAAK,KAAK,CAAC;QACX,KAAK,IAAI;YACP,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAA;YACxE,OAAM;QACR,KAAK,KAAK;YACR,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;YAC9C,OAAM;QACR,KAAK,IAAI;YACP,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;YACzC,OAAM;QACR;YACE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAA;YAC3C,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;IACjD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAQ;IAChC,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAC/C,QAAQ,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC/C,MAAM,OAAO,GAAU,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAClC,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,CAAC,EACD,UAAU,KAAK,mCAAmC,EAClD,0FAA0F,EAC1F;oBACE,OAAO,EAAE,8DAA8D;oBACvE,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,IAAI;iBACZ,CACF,CAAA;gBACH,MAAM,SAAS,GAAG,CAAC,EAA0B,EAAE,OAAW,EAAE,EAAE;oBAC5D,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;wBAC/C,MAAM,OAAO,GAAsB,WAAW,CAAC,KAAiC,CAAC,IAAI,EAAE,CAAA;wBACvF,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;4BACtE,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gCAAE,OAAM;4BACnC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;4BACrC,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,CAAC,EACD,IAAI,KAAK,+BAA+B,KAAK,KAAK,UAAU,CAAC,KAAK,CAAC,EAAE,EACrE,OAAO,CAAC,MAAM;gCACZ,CAAC,CAAC,uBAAuB,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gCACxD,CAAC,CAAC,GAAG,KAAK,8BAA8B,EAC1C;gCACE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,uCAAuC;gCACjF,OAAO,EAAE,IAAI;gCACb,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;6BAC5E,CACF,CAAA;wBACH,CAAC,CAAC,CAAA;oBACJ,CAAC;gBACH,CAAC,CAAA;gBACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;oBAClB,KAAK,QAAQ;wBACX,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;wBAClD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAU;4BAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gCAAE,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;wBAChG,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;4BACjD,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gCACtE,IAAI,KAAK,KAAK,QAAQ;oCACpB,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,CAAC,EACD,iBAAiB,KAAK,2BAA2B,EACjD,sGAAsG,CACvG,CAAA;4BACL,CAAC,CAAC,CAAA;wBACJ,OAAM;oBACR,KAAK,IAAI;wBACP,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAU;4BACpD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gCAAE,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;wBAChG,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;wBAC3B,OAAM;oBACR,KAAK,MAAM,CAAC;oBACZ,KAAK,MAAM;wBACT,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;wBAClD,OAAM;oBACR,KAAK,IAAI;wBACP,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;wBACrD,OAAM;oBACR,KAAK,QAAQ;wBACX,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;wBAC3B,OAAM;oBACR,KAAK,MAAM;wBACT,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;wBACpD,OAAM;oBACR,KAAK,OAAO;wBACV,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;wBAClD,OAAM;oBACR;wBACE,OAAM;gBACV,CAAC;YACH,CAAC,CAAC,CAAA;AACR,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function declaredErrors(ctx: Ctx): void;
3
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/rules/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAQxC,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,QAkHtC"}
@@ -0,0 +1,97 @@
1
+ import { at, resolveAt } from '@hozu/core/ir';
2
+ import { effectSchemas } from '../env.js';
3
+ import { resolveRef } from '../resolve.js';
4
+ import { closest, didYouMean } from '../suggest.js';
5
+ import { featurePointer, walkView } from '../walk.js';
6
+ const required = (errors) => [...Object.keys(errors), 'Unexpected'];
7
+ export function declaredErrors(ctx) {
8
+ for (const f of Object.values(ctx.ir.features)) {
9
+ const initial = f.machine?.initial ?? '';
10
+ for (const [state, s] of Object.entries(f.machine?.states ?? {})) {
11
+ if (!s.invoke)
12
+ continue;
13
+ const schemas = effectSchemas(ctx.ir, s.invoke.effect);
14
+ if (!schemas)
15
+ continue;
16
+ const names = required(schemas.errors);
17
+ const base = featurePointer(f.id, 'machine', 'states', state, 'invoke', 'failed');
18
+ const template = s.invoke.failed.Unexpected?.length
19
+ ? s.invoke.failed.Unexpected
20
+ : [{ guard: null, target: initial, assign: [], navigate: null }];
21
+ for (const name of names) {
22
+ if (s.invoke.failed[name]?.length)
23
+ continue;
24
+ ctx.report('HZ004', f.id, at(base, name), `"${state}" invokes ${s.invoke.effect} but does not handle its "${name}" error`, `${s.invoke.effect} declares errors ${names.join(', ')}; every one needs a failed transition.`, {
25
+ summary: `Add failed.${name}`,
26
+ snippet: `failed: { ${name}: [{ target: '${template[0].target}' }] }`,
27
+ patch: [{ op: 'add', path: resolveAt(at(base, name)), value: template }],
28
+ });
29
+ }
30
+ for (const name of Object.keys(s.invoke.failed)) {
31
+ if (names.includes(name) || (name === 'Invalid' && schemas.invalid))
32
+ continue;
33
+ const guess = closest(name, names);
34
+ ctx.report('HZ007', f.id, at(base, name), `${s.invoke.effect} has no error "${name}".${didYouMean(guess)}`, `Declared errors: ${names.join(', ')}.`, {
35
+ summary: 'Remove the handler',
36
+ snippet: null,
37
+ patch: [{ op: 'remove', path: resolveAt(at(base, name)) }],
38
+ });
39
+ }
40
+ }
41
+ for (const [vid, view] of Object.entries(f.views))
42
+ walkView(ctx.ir, f, vid, view, ({ node, pointer }) => {
43
+ if (node.kind !== 'query')
44
+ return;
45
+ const r = resolveRef(ctx.ir, node.query, 'query');
46
+ if (!r)
47
+ return;
48
+ const names = required(r.feature.queries[r.symbol].errors);
49
+ for (const name of names) {
50
+ if (node.failed[name])
51
+ continue;
52
+ const template = node.failed.Unexpected ?? {
53
+ id: `${node.id}/failed/${name}`,
54
+ kind: 'text',
55
+ value: { literal: '' },
56
+ };
57
+ ctx.report('HZ004', f.id, at(pointer, 'failed', name), `Query ${node.query} can fail with "${name}" but the view does not render it`, `Declared errors: ${names.join(', ')}.`, {
58
+ summary: `Add failed.${name}`,
59
+ snippet: `failed: { ${name}: () => ui.p({}, ['…']) }`,
60
+ patch: [
61
+ {
62
+ op: 'add',
63
+ path: resolveAt(at(pointer, 'failed', name)),
64
+ value: template,
65
+ },
66
+ ],
67
+ });
68
+ }
69
+ for (const name of Object.keys(node.failed))
70
+ if (!names.includes(name))
71
+ ctx.report('HZ007', f.id, at(pointer, 'failed', name), `${node.query} has no error "${name}"`, `Declared errors: ${names.join(', ')}.`, {
72
+ summary: 'Remove the branch',
73
+ snippet: null,
74
+ patch: [{ op: 'remove', path: resolveAt(at(pointer, 'failed', name)) }],
75
+ });
76
+ });
77
+ for (const [cid, c] of Object.entries(f.contracts))
78
+ c.when.forEach((step, i) => {
79
+ if (!('failed' in step))
80
+ return;
81
+ const schemas = effectSchemas(ctx.ir, step.failed);
82
+ if (!schemas)
83
+ return;
84
+ const names = required(schemas.errors);
85
+ if (names.includes(step.error) || (step.error === 'Invalid' && schemas.invalid))
86
+ return;
87
+ const guess = closest(step.error, names);
88
+ const p = featurePointer(f.id, 'contracts', cid, 'when', i, 'error');
89
+ ctx.report('HZ007', f.id, p, `${step.failed} has no error "${step.error}".${didYouMean(guess)}`, `Declared errors: ${names.join(', ')}.`, {
90
+ summary: guess ? `Use "${guess}"` : 'Use a declared error',
91
+ snippet: null,
92
+ patch: guess ? [{ op: 'replace', path: resolveAt(p), value: guess }] : null,
93
+ });
94
+ });
95
+ }
96
+ }
97
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/rules/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function unhandledEvents(ctx: Ctx): void;
3
+ export declare function viewEvents(ctx: Ctx): void;
4
+ export declare function conflictingIgnores(ctx: Ctx): void;
5
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/rules/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAIxC,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,QAkCvC;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,QA4ClC;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,QAoB1C"}
@@ -0,0 +1,87 @@
1
+ import { at, resolveAt } from '@hozu/core/ir';
2
+ import { resolveRef } from '../resolve.js';
3
+ import { featurePointer, walkView } from '../walk.js';
4
+ export function unhandledEvents(ctx) {
5
+ for (const f of Object.values(ctx.ir.features)) {
6
+ const states = Object.values(f.machine?.states ?? {});
7
+ for (const symbol of Object.keys(f.events)) {
8
+ const ref = `${f.id}.${symbol}`;
9
+ if (states.some((s) => s.on[ref]?.length))
10
+ continue;
11
+ const p = featurePointer(f.id, 'events', symbol);
12
+ const exported = f.exports.events.indexOf(symbol);
13
+ ctx.report('HZ002', f.id, p, `Event ${ref} is declared but no state handles it`, f.machine
14
+ ? `No state of the ${f.id} machine has on(${symbol}, …).`
15
+ : `${f.id} has no machine, so its events can never be handled.`, {
16
+ summary: `Handle ${symbol} in a state, or remove the event`,
17
+ snippet: `on(${symbol}, { target: '${f.machine?.initial ?? 'someState'}' })`,
18
+ patch: [
19
+ ...(exported >= 0
20
+ ? [
21
+ {
22
+ op: 'remove',
23
+ path: resolveAt(featurePointer(f.id, 'exports', 'events', exported)),
24
+ },
25
+ ]
26
+ : []),
27
+ { op: 'remove', path: resolveAt(p) },
28
+ ],
29
+ });
30
+ }
31
+ }
32
+ }
33
+ export function viewEvents(ctx) {
34
+ for (const f of Object.values(ctx.ir.features))
35
+ for (const [vid, view] of Object.entries(f.views))
36
+ walkView(ctx.ir, f, vid, view, ({ node, pointer, visible }) => {
37
+ if (node.kind !== 'el' && node.kind !== 'widget' && node.kind !== 'global')
38
+ return;
39
+ for (const [dom, send] of Object.entries(node.on)) {
40
+ const r = resolveRef(ctx.ir, send.event, 'event');
41
+ const m = r?.feature.machine;
42
+ if (!r || !m)
43
+ continue;
44
+ const own = r.feature.id === f.id && visible !== null;
45
+ const shown = own ? visible : Object.keys(m.states);
46
+ const handling = new Set(Object.entries(m.states)
47
+ .filter(([, s]) => s.on[send.event]?.length || s.ignore.includes(send.event))
48
+ .map(([n]) => n));
49
+ const missing = shown.filter((s) => !handling.has(s));
50
+ if (!missing.length)
51
+ continue;
52
+ ctx.report('HZ005', f.id, at(pointer, 'on', dom), `${send.event} can be sent while ${r.feature.id} is in ${missing.map((s) => `"${s}"`).join(', ')}, where it is not handled`, own
53
+ ? 'A node may only send an event when every state in which it is visible handles that event.'
54
+ : `The sender cannot observe the state of ${r.feature.id}, so every one of its states must handle ${send.event}.`, own
55
+ ? {
56
+ summary: `Ignore ${send.event} in ${missing.join(', ')} (ignore: [${send.event.split('.')[1]}]), or show the node only in the states that handle it`,
57
+ snippet: `ignore: [${send.event.split('.')[1]}]`,
58
+ patch: missing.map((s) => ({
59
+ op: 'add',
60
+ path: resolveAt(featurePointer(r.feature.id, 'machine', 'states', s, 'ignore', '-')),
61
+ value: send.event,
62
+ })),
63
+ }
64
+ : {
65
+ summary: `Handle ${send.event} in ${missing.join(', ')}, or stop sending it from here`,
66
+ snippet: null,
67
+ patch: [{ op: 'remove', path: resolveAt(at(pointer, 'on', dom)) }],
68
+ });
69
+ }
70
+ });
71
+ }
72
+ export function conflictingIgnores(ctx) {
73
+ for (const f of Object.values(ctx.ir.features))
74
+ for (const [name, s] of Object.entries(f.machine?.states ?? {}))
75
+ s.ignore.forEach((event, i) => {
76
+ if (!s.on[event]?.length)
77
+ return;
78
+ ctx.report('HZ034', f.id, featurePointer(f.id, 'machine', 'states', name, 'ignore', i), `State "${name}" both handles and ignores ${event}`, 'An ignored event is dropped; a handled one takes a transition. A state must choose one.', {
79
+ summary: `Remove ${event} from ignore`,
80
+ snippet: null,
81
+ patch: [
82
+ { op: 'remove', path: resolveAt(featurePointer(f.id, 'machine', 'states', name, 'ignore', i)) },
83
+ ],
84
+ });
85
+ });
86
+ }
87
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/rules/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErD,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAA;QACrD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,IAAI,MAAM,EAAE,CAAA;YAC/B,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;gBAAE,SAAQ;YACnD,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;YAChD,MAAM,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YACjD,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,CAAC,EACD,SAAS,GAAG,sCAAsC,EAClD,CAAC,CAAC,OAAO;gBACP,CAAC,CAAC,mBAAmB,CAAC,CAAC,EAAE,mBAAmB,MAAM,OAAO;gBACzD,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,sDAAsD,EACjE;gBACE,OAAO,EAAE,UAAU,MAAM,kCAAkC;gBAC3D,OAAO,EAAE,MAAM,MAAM,gBAAgB,CAAC,CAAC,OAAO,EAAE,OAAO,IAAI,WAAW,MAAM;gBAC5E,KAAK,EAAE;oBACL,GAAG,CAAC,QAAQ,IAAI,CAAC;wBACf,CAAC,CAAC;4BACE;gCACE,EAAE,EAAE,QAAiB;gCACrB,IAAI,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;6BACrE;yBACF;wBACH,CAAC,CAAC,EAAE,CAAC;oBACP,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE;iBACrC;aACF,CACF,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAQ;IACjC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QAC5C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC5D,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAM;gBAClF,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAClD,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;oBACjD,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO,CAAA;oBAC5B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;wBAAE,SAAQ;oBACtB,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,OAAO,KAAK,IAAI,CAAA;oBACrD,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;oBACnD,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;yBACrB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;yBAC5E,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACnB,CAAA;oBACD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;oBACrD,IAAI,CAAC,OAAO,CAAC,MAAM;wBAAE,SAAQ;oBAC7B,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EACtB,GAAG,IAAI,CAAC,KAAK,sBAAsB,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAC3H,GAAG;wBACD,CAAC,CAAC,2FAA2F;wBAC7F,CAAC,CAAC,0CAA0C,CAAC,CAAC,OAAO,CAAC,EAAE,4CAA4C,IAAI,CAAC,KAAK,GAAG,EACnH,GAAG;wBACD,CAAC,CAAC;4BACE,OAAO,EAAE,UAAU,IAAI,CAAC,KAAK,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,wDAAwD;4BACpJ,OAAO,EAAE,YAAY,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG;4BAChD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gCACzB,EAAE,EAAE,KAAc;gCAClB,IAAI,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;gCACpF,KAAK,EAAE,IAAI,CAAC,KAAK;6BAClB,CAAC,CAAC;yBACJ;wBACH,CAAC,CAAC;4BACE,OAAO,EAAE,UAAU,IAAI,CAAC,KAAK,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC;4BACtF,OAAO,EAAE,IAAI;4BACb,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;yBACnE,CACN,CAAA;gBACH,CAAC;YACH,CAAC,CAAC,CAAA;AACR,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAQ;IACzC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC;YAC7D,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBAC5B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM;oBAAE,OAAM;gBAChC,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,EAC5D,UAAU,IAAI,8BAA8B,KAAK,EAAE,EACnD,yFAAyF,EACzF;oBACE,OAAO,EAAE,UAAU,KAAK,cAAc;oBACtC,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE;wBACL,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;qBAChG;iBACF,CACF,CAAA;YACH,CAAC,CAAC,CAAA;AACR,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function progressiveForms(ctx: Ctx): void;
3
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../../src/rules/forms.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAGxC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,QAqBxC"}
@@ -0,0 +1,19 @@
1
+ import { at, formRunnable } from '@hozu/core/ir';
2
+ import { walkView } from '../walk.js';
3
+ export function progressiveForms(ctx) {
4
+ const { ir } = ctx;
5
+ for (const f of Object.values(ir.features))
6
+ for (const [vid, view] of Object.entries(f.views))
7
+ walkView(ir, f, vid, view, ({ node, pointer }) => {
8
+ if (node.kind !== 'el' || node.tag !== 'form' || !node.on.submit)
9
+ return;
10
+ if (formRunnable(node.on.submit.payload))
11
+ return;
12
+ ctx.report('HZ036', f.id, at(pointer, 'on', 'submit', 'payload'), `This form only works with JavaScript: its ${node.on.submit.event} payload reads values the server cannot see`, 'Without JavaScript the browser posts the named form fields; the server can use those, context, params and search, but not other DOM fields or each/query bindings.', {
13
+ summary: "Read the fields with ui.dom.form('name') so the form also works before hydration and without JavaScript",
14
+ snippet: null,
15
+ patch: null,
16
+ });
17
+ });
18
+ }
19
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/rules/forms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACvC,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAC/C,QAAQ,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;gBAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM;oBAAE,OAAM;gBACxE,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;oBAAE,OAAM;gBAChD,GAAG,CAAC,MAAM,CACR,OAAO,EACP,CAAC,CAAC,EAAE,EACJ,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,EACtC,6CAA6C,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,6CAA6C,EAC9G,oKAAoK,EACpK;oBACE,OAAO,EACL,yGAAyG;oBAC3G,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,IAAI;iBACZ,CACF,CAAA;YACH,CAAC,CAAC,CAAA;AACR,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function unsafeHtml(ctx: Ctx): void;
3
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/rules/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAaxC,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,QAsDlC"}
@@ -0,0 +1,55 @@
1
+ import { anyRef, at, resolveAt } from '@hozu/core/ir';
2
+ import { featurePointer } from '../walk.js';
3
+ const untrusted = (v, tainted) => anyRef(v, (r) => r.ref === 'context' ||
4
+ r.ref === 'dom' ||
5
+ r.ref === 'params' ||
6
+ (r.ref === 'binding' && tainted[r.depth] === true));
7
+ export function unsafeHtml(ctx) {
8
+ for (const f of Object.values(ctx.ir.features))
9
+ for (const [vid, view] of Object.entries(f.views)) {
10
+ const walk = (n, pointer, tainted) => {
11
+ const kids = (list, key) => list.forEach((c, i) => walk(c, at(pointer, key, i), tainted));
12
+ switch (n.kind) {
13
+ case 'html':
14
+ if (untrusted(n.value, tainted))
15
+ ctx.report('HZ030', f.id, at(pointer, 'value'), 'ui.html renders a value the client or URL controls', 'Context, DOM fields and route params can carry user input; rendering them as HTML allows script injection (XSS). Only query results (sanitized by the server) and literals may be rendered as HTML.', {
16
+ summary: 'Render it as text, or sanitize it on the server and read it from a query',
17
+ snippet: null,
18
+ patch: [
19
+ {
20
+ op: 'replace',
21
+ path: resolveAt(pointer),
22
+ value: { id: n.id, kind: 'text', value: n.value },
23
+ },
24
+ ],
25
+ });
26
+ return;
27
+ case 'el':
28
+ case 'when':
29
+ case 'widget':
30
+ kids(n.children, 'children');
31
+ return;
32
+ case 'if':
33
+ kids(n.ifTrue, 'ifTrue');
34
+ kids(n.ifFalse, 'ifFalse');
35
+ return;
36
+ case 'each':
37
+ walk(n.item, at(pointer, 'item'), [...tainted, untrusted(n.source, tainted)]);
38
+ return;
39
+ case 'query': {
40
+ const input = untrusted(n.input, tainted);
41
+ walk(n.ready, at(pointer, 'ready'), [...tainted, false]);
42
+ if (n.pending)
43
+ walk(n.pending, at(pointer, 'pending'), tainted);
44
+ for (const [name, c] of Object.entries(n.failed))
45
+ walk(c, at(pointer, 'failed', name), [...tainted, input]);
46
+ return;
47
+ }
48
+ default:
49
+ return;
50
+ }
51
+ };
52
+ walk(view.root, featurePointer(f.id, 'views', vid, 'root'), []);
53
+ }
54
+ }
55
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/rules/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function httpRules(ctx: Ctx): void;
3
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/rules/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAoBxC,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,QAoFjC"}
@@ -0,0 +1,71 @@
1
+ import { join, routePattern } from '@hozu/core/ir';
2
+ const RESERVED = new Set([
3
+ 'content-type',
4
+ 'content-length',
5
+ 'cache-control',
6
+ 'vary',
7
+ 'location',
8
+ 'set-cookie',
9
+ 'content-security-policy',
10
+ 'x-hozu-cache',
11
+ ]);
12
+ const TOKEN = /^[a-z0-9!#$%&'*+.^_`|~-]+$/;
13
+ const BASE = /^(\/[A-Za-z0-9._~-]+)+$/;
14
+ const overlaps = (a, b) => {
15
+ const [x, y] = [routePattern(a), routePattern(b)];
16
+ return x.pattern.test(y.sample) || y.pattern.test(x.sample);
17
+ };
18
+ export function httpRules(ctx) {
19
+ const { http } = ctx.ir;
20
+ if (http.basePath !== '' && !BASE.test(http.basePath))
21
+ ctx.report('HZ039', null, join('', 'http', 'basePath'), `basePath "${http.basePath}" is not a path prefix`, 'The prefix is joined to every URL, so it must be empty or /segment[/segment…] without a trailing slash.', {
22
+ summary: 'Use a prefix such as "/shop"',
23
+ snippet: null,
24
+ patch: [
25
+ {
26
+ op: 'replace',
27
+ path: join('', 'http', 'basePath'),
28
+ value: `/${http.basePath.split('/').filter(Boolean).join('/')}`.replace(/^\/$/, ''),
29
+ },
30
+ ],
31
+ });
32
+ const pages = Object.keys(ctx.ir.pages).flatMap((id) => ctx.ir.routes[id] ? [[id, ctx.ir.routes[id].path]] : []);
33
+ http.redirects.forEach((r, i) => {
34
+ const at = join('', 'http', 'redirects', i);
35
+ const remove = {
36
+ summary: 'Remove this redirect',
37
+ snippet: null,
38
+ patch: [{ op: 'remove', path: at }],
39
+ };
40
+ const invalid = (message, cause) => ctx.report('HZ037', null, at, message, cause, remove);
41
+ if (!r.from.startsWith('/'))
42
+ return invalid(`Redirect "${r.from}" is not a path`, 'A redirect matches a request path.');
43
+ const page = pages.find(([, path]) => overlaps(r.from, path));
44
+ if (page)
45
+ return invalid(`Redirect "${r.from}" matches route "${page[0]}" (${page[1]})`, 'A URL has one owner: a redirect never hides a page.');
46
+ const other = http.redirects.slice(0, i).find((o) => overlaps(r.from, o.from));
47
+ if (other)
48
+ return invalid(`Redirect "${r.from}" overlaps redirect "${other.from}"`, 'Two redirects for one URL make the target depend on order.');
49
+ if ('link' in r.to && !ctx.ir.routes[r.to.link])
50
+ return invalid(`Redirect "${r.from}" targets an unknown route`, 'Point `to` at a registered route with ui.link.');
51
+ if ('literal' in r.to && typeof r.to.literal === 'string' && r.to.literal.startsWith('/'))
52
+ ctx.report('HZ032', null, join(at, 'to'), `Redirect "${r.from}" targets the internal path "${r.to.literal}" as a string`, 'Internal URLs are built from route declarations so they stay typed and canonical.', { summary: 'Use to: (params) => ui.link(route, params)', snippet: null, patch: null });
53
+ });
54
+ http.headers.forEach((rule, i) => {
55
+ for (const [name, value] of Object.entries(rule.set)) {
56
+ const at = join('', 'http', 'headers', i, 'set', name);
57
+ const why = RESERVED.has(name)
58
+ ? `Header "${name}" is owned by the framework`
59
+ : !TOKEN.test(name)
60
+ ? `Header name "${name}" is not a lowercase token`
61
+ : /[\r\n]/.test(value)
62
+ ? `Header "${name}" has a line break in its value`
63
+ : null;
64
+ if (why)
65
+ ctx.report('HZ038', null, at, why, RESERVED.has(name)
66
+ ? 'It is derived (cache-control, content-type), set by sessions (set-cookie) or configured on the server (content-security-policy).'
67
+ : 'Header names are lowercase tokens and values are one line.', { summary: 'Remove the header', snippet: null, patch: [{ op: 'remove', path: at }] });
68
+ }
69
+ });
70
+ }
71
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/rules/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGlD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,MAAM;IACN,UAAU;IACV,YAAY;IACZ,yBAAyB;IACzB,cAAc;CACf,CAAC,CAAA;AACF,MAAM,KAAK,GAAG,4BAA4B,CAAA;AAC1C,MAAM,IAAI,GAAG,yBAAyB,CAAA;AAEtC,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;IACxC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,CAAC,CAAA;AAED,MAAM,UAAU,SAAS,CAAC,GAAQ;IAChC,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,EAAE,CAAA;IACvB,IAAI,IAAI,CAAC,QAAQ,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;QACnD,GAAG,CAAC,MAAM,CACR,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAC5B,aAAa,IAAI,CAAC,QAAQ,wBAAwB,EAClD,yGAAyG,EACzG;YACE,OAAO,EAAE,8BAA8B;YACvC,OAAO,EAAE,IAAI;YACb,KAAK,EAAE;gBACL;oBACE,EAAE,EAAE,SAAS;oBACb,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC;oBAClC,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;iBACpF;aACF;SACF,CACF,CAAA;IACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CACrD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CACzD,CAAA;IACD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAA;QAC3C,MAAM,MAAM,GAAG;YACb,OAAO,EAAE,sBAAsB;YAC/B,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAiB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;SAC7C,CAAA;QACD,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,KAAa,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACzG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACzB,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,iBAAiB,EAAE,oCAAoC,CAAC,CAAA;QAC5F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,IAAK,CAAC,CAAC,CAAA;QAC9D,IAAI,IAAI;YACN,OAAO,OAAO,CACZ,aAAa,CAAC,CAAC,IAAI,oBAAoB,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,GAAG,EAC9D,qDAAqD,CACtD,CAAA;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QAC9E,IAAI,KAAK;YACP,OAAO,OAAO,CACZ,aAAa,CAAC,CAAC,IAAI,wBAAwB,KAAK,CAAC,IAAI,GAAG,EACxD,4DAA4D,CAC7D,CAAA;QACH,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC;YAC7C,OAAO,OAAO,CACZ,aAAa,CAAC,CAAC,IAAI,4BAA4B,EAC/C,gDAAgD,CACjD,CAAA;QACH,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACvF,GAAG,CAAC,MAAM,CACR,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EACd,aAAa,CAAC,CAAC,IAAI,gCAAgC,CAAC,CAAC,EAAE,CAAC,OAAO,eAAe,EAC9E,mFAAmF,EACnF,EAAE,OAAO,EAAE,4CAA4C,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CACtF,CAAA;IACL,CAAC,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACrD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;YACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAC5B,CAAC,CAAC,WAAW,IAAI,6BAA6B;gBAC9C,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;oBACjB,CAAC,CAAC,gBAAgB,IAAI,4BAA4B;oBAClD,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;wBACpB,CAAC,CAAC,WAAW,IAAI,iCAAiC;wBAClD,CAAC,CAAC,IAAI,CAAA;YACZ,IAAI,GAAG;gBACL,GAAG,CAAC,MAAM,CACR,OAAO,EACP,IAAI,EACJ,EAAE,EACF,GAAG,EACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;oBAChB,CAAC,CAAC,kIAAkI;oBACpI,CAAC,CAAC,4DAA4D,EAChE,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CACrF,CAAA;QACL,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '../context.ts';
2
+ export declare function i18n(ctx: Ctx): void;
3
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/rules/i18n.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AA+ExC,wBAAgB,IAAI,CAAC,GAAG,EAAE,GAAG,QA4K5B"}