@prestack/thread 0.1.20 → 0.1.22

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 (148) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/dist/core/attach.js +21 -0
  3. package/dist/core/descend.d.ts +2 -0
  4. package/dist/core/descend.js +18 -3
  5. package/dist/core/generators/commit.js +55 -3
  6. package/dist/core/generators/dev.d.ts +1 -0
  7. package/dist/core/generators/dev.js +68 -0
  8. package/dist/core/rows.d.ts +5 -0
  9. package/dist/core/rows.js +45 -0
  10. package/dist/core/tree.d.ts +1 -0
  11. package/dist/core/tree.js +7 -1
  12. package/dist/derivation/generators/build.d.ts +3 -1
  13. package/dist/derivation/generators/build.js +103 -22
  14. package/dist/derivation/generators/release.js +74 -26
  15. package/dist/derivation/release.js +3 -3
  16. package/dist/integrations/cli.d.ts +3 -9
  17. package/dist/integrations/cli.js +96 -36
  18. package/dist/integrations/declared.d.ts +8 -0
  19. package/dist/integrations/declared.js +22 -0
  20. package/dist/integrations/pglite.js +13 -3
  21. package/dist/integrations/target.d.ts +4 -1
  22. package/dist/integrations/target.js +20 -6
  23. package/dist/integrity/attach.js +1 -5
  24. package/dist/integrity/generators/publish.js +2 -1
  25. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  26. package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
  27. package/dist/operations/access.js +3 -2
  28. package/dist/operations/capabilities.d.ts +2 -0
  29. package/dist/operations/capabilities.js +2 -0
  30. package/dist/operations/generators/capability.js +234 -24
  31. package/dist/operations/generators/compile.d.ts +21 -0
  32. package/dist/operations/generators/compile.js +151 -51
  33. package/dist/operations/generators/permits.d.ts +2 -1
  34. package/dist/operations/generators/permits.js +52 -47
  35. package/dist/operations/generators/seed.d.ts +3 -0
  36. package/dist/operations/generators/seed.js +25 -3
  37. package/dist/operations/nodes.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/dist/core/actor.ts +0 -21
  40. package/dist/core/attach.ts +0 -613
  41. package/dist/core/chain.ts +0 -145
  42. package/dist/core/condition.ts +0 -227
  43. package/dist/core/descend.ts +0 -1173
  44. package/dist/core/errors/catalogue.ts +0 -314
  45. package/dist/core/errors/classes.ts +0 -51
  46. package/dist/core/errors/error.ts +0 -152
  47. package/dist/core/errors/namespaces.ts +0 -19
  48. package/dist/core/finding.ts +0 -37
  49. package/dist/core/generators/commit.ts +0 -157
  50. package/dist/core/init.ts +0 -222
  51. package/dist/core/invoked.ts +0 -52
  52. package/dist/core/round.ts +0 -74
  53. package/dist/core/rows.ts +0 -459
  54. package/dist/core/scope.ts +0 -20
  55. package/dist/core/thread.ts +0 -29
  56. package/dist/core/tree.ts +0 -90
  57. package/dist/core/types.ts +0 -351
  58. package/dist/derivation/attach.ts +0 -57
  59. package/dist/derivation/generators/build.ts +0 -371
  60. package/dist/derivation/generators/check.ts +0 -118
  61. package/dist/derivation/generators/determinism.ts +0 -145
  62. package/dist/derivation/generators/release.ts +0 -236
  63. package/dist/derivation/release.ts +0 -43
  64. package/dist/integrations/attach.ts +0 -187
  65. package/dist/integrations/cli.ts +0 -615
  66. package/dist/integrations/disk.ts +0 -466
  67. package/dist/integrations/generators/read.ts +0 -127
  68. package/dist/integrations/generators/vault.ts +0 -210
  69. package/dist/integrations/https.ts +0 -216
  70. package/dist/integrations/pglite.ts +0 -206
  71. package/dist/integrations/secret.ts +0 -54
  72. package/dist/integrations/target.ts +0 -656
  73. package/dist/integrations/vault.ts +0 -148
  74. package/dist/integrity/attach.ts +0 -150
  75. package/dist/integrity/check-threads.ts +0 -166
  76. package/dist/integrity/constraint.ts +0 -1246
  77. package/dist/integrity/generators/compiler.ts +0 -138
  78. package/dist/integrity/generators/gate.ts +0 -227
  79. package/dist/integrity/generators/issues.ts +0 -160
  80. package/dist/integrity/generators/publish.ts +0 -232
  81. package/dist/integrity/generators/trial.ts +0 -359
  82. package/dist/integrity/lint.ts +0 -214
  83. package/dist/integrity/lsp/bin.ts +0 -44
  84. package/dist/integrity/lsp/diagnostics.ts +0 -92
  85. package/dist/integrity/lsp/findings.ts +0 -63
  86. package/dist/integrity/lsp/lint.ts +0 -124
  87. package/dist/integrity/lsp/protocol.ts +0 -78
  88. package/dist/integrity/lsp/server.ts +0 -224
  89. package/dist/integrity/slopnator.ts +0 -324
  90. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  91. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  92. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  93. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  94. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  95. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  96. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  97. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  98. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  99. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  100. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  101. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  102. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  103. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  104. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  105. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  106. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  107. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  108. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  109. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  110. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  111. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  112. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  113. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  114. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  115. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  116. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  117. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  118. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  119. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  120. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  121. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  122. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  123. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  124. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  125. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  126. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  127. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  128. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  129. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  130. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  131. package/dist/integrity/sweep.ts +0 -395
  132. package/dist/knowledge/attach.ts +0 -327
  133. package/dist/knowledge/generators/docsgen.ts +0 -186
  134. package/dist/knowledge/generators/glossary.ts +0 -73
  135. package/dist/knowledge/glossary.ts +0 -6
  136. package/dist/operations/access.ts +0 -131
  137. package/dist/operations/attach.ts +0 -408
  138. package/dist/operations/capabilities.ts +0 -88
  139. package/dist/operations/dispatch.ts +0 -294
  140. package/dist/operations/error-results.ts +0 -31
  141. package/dist/operations/generators/capability.ts +0 -1054
  142. package/dist/operations/generators/compile.ts +0 -701
  143. package/dist/operations/generators/permits.ts +0 -224
  144. package/dist/operations/generators/seed.ts +0 -359
  145. package/dist/operations/generators/trial.ts +0 -666
  146. package/dist/operations/nodes.ts +0 -53
  147. package/dist/operations/operations.ts +0 -91
  148. package/dist/operations/result.ts +0 -28
@@ -1,152 +0,0 @@
1
- import type { Slopnator } from '../slopnator.ts'
2
-
3
- import { descend, read, reached } from '../../core/descend.ts'
4
-
5
- import { rowsIn } from './sl_unthreaded.ts'
6
-
7
- const ELEMENT = 'element'
8
-
9
- const VARIANT = 'variant'
10
-
11
- interface Worn {
12
- readonly elements: ReadonlySet<string>
13
- readonly variants: ReadonlyMap<string, readonly string[]>
14
- }
15
-
16
- function nameOf(id: string): string {
17
- return id
18
- .split('-')
19
- .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
20
- .join('')
21
- }
22
-
23
- function idOf(name: string): string {
24
- return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
25
- }
26
-
27
- function memberOf(id: string): string {
28
- const [first = '', ...rest] = id.split('-')
29
- return first + rest.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join('')
30
- }
31
-
32
- function declared(): Worn {
33
- const elements = new Set<string>()
34
- const variants = new Map<string, readonly string[]>()
35
-
36
- descend()
37
-
38
- for (const at of reached()) {
39
- const text = read(at)
40
- if (text === undefined) continue
41
-
42
- let waiting: { readonly id: string; readonly wears?: readonly string[] }[] = []
43
- for (const one of rowsIn(text)) {
44
- if (one.id === undefined) continue
45
- if (one.list === 'declares') {
46
- waiting.push(one.wears === undefined ? { id: one.id } : { id: one.id, wears: one.wears })
47
- continue
48
- }
49
- if (one.list !== undefined) continue
50
- if (one.id === ELEMENT) for (const row of waiting) elements.add(nameOf(row.id))
51
- if (one.id === VARIANT) for (const row of waiting) variants.set(row.id, row.wears ?? [])
52
- waiting = []
53
- }
54
- }
55
-
56
- return { elements, variants }
57
- }
58
-
59
- interface Tag {
60
- readonly type?: string
61
- readonly object?: { readonly type?: string; readonly name?: string }
62
- readonly property?: { readonly type?: string; readonly name?: string }
63
- }
64
-
65
- export const slopnator: Slopnator = {
66
- on: ['JSXOpeningElement'],
67
- holds: (node) => {
68
- const name = (node as { name?: Tag } | undefined)?.name
69
- if (name?.type !== 'JSXMemberExpression') return false
70
-
71
- const element = name.object?.type === 'JSXIdentifier' ? name.object.name : undefined
72
- const member = name.property?.type === 'JSXIdentifier' ? name.property.name : undefined
73
- if (element === undefined || member === undefined) return false
74
-
75
- const { elements, variants } = declared()
76
- if (!elements.has(element)) return false
77
-
78
- const id = idOf(member)
79
- const wears = variants.get(id)
80
-
81
- const worn = [...variants]
82
- .filter(([, all]) => all.includes(element))
83
- .map(([one]) => `${element}.${memberOf(one)}`)
84
- .sort()
85
-
86
- const offered =
87
- worn.length === 0
88
- ? `nothing declares itself worn by \`${element}\`, so no member of it would have been accepted — a variant row has to name \`${element}\` first`
89
- : `\`${element}\` wears ${worn.join(', ')}`
90
-
91
- if (wears === undefined) {
92
- return (
93
- `\`${element}.${member}\` names the variant \`${id}\` and nothing declares it — so it renders carrying a class ` +
94
- 'the stylesheet never writes, which reads on screen as an element that lost its styling and is reported ' +
95
- `nowhere. ${offered}`
96
- )
97
- }
98
-
99
- if (!wears.includes(element)) {
100
- return (
101
- `\`${element}.${member}\` pairs \`${element}\` with the variant \`${id}\`, and that variant's row does not name ` +
102
- `\`${element}\` among the elements that wear it — so the pairing is nobody's declaration. ${offered}`
103
- )
104
- }
105
-
106
- return false
107
- },
108
- proves: {
109
- wrong: [
110
- {
111
- files: [
112
- { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
113
- { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_worn.ts',\n },\n ],\n}\n" },
114
- {
115
- at: 'threads/_probe_worn.ts',
116
- source:
117
- "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const ELEMENTS: Threaded = {\n id: 'element',\n declares: [\n {\n id: 'button',\n at: 'app/_probe_button.tsx',\n },\n ],\n}\n\nexport const VARIANTS: Threaded = {\n id: 'variant',\n declares: [\n {\n id: 'button-primary',\n at: 'app/_probe_variant.ts',\n wears: ['Button'],\n },\n ],\n}\n",
118
- },
119
- { at: 'app/_probe_undeclared.tsx', source: 'export const HELD = <Button.primary />\n' },
120
- ],
121
- at: 'app/_probe_undeclared.tsx',
122
- },
123
- {
124
- files: [
125
- { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n readonly seeds?: readonly Threaded[]\n}\n" },
126
- { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n {\n at: 'threads/_probe_worn.ts',\n },\n ],\n}\n" },
127
- {
128
- at: 'threads/_probe_worn.ts',
129
- source:
130
- "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const ELEMENTS: Threaded = {\n id: 'element',\n declares: [\n {\n id: 'button',\n at: 'app/_probe_button.tsx',\n },\n {\n id: 'badge',\n at: 'app/_probe_badge.tsx',\n },\n ],\n}\n\nexport const VARIANTS: Threaded = {\n id: 'variant',\n declares: [\n {\n id: 'chip',\n at: 'app/_probe_chip.ts',\n wears: ['Badge'],\n },\n ],\n}\n",
131
- },
132
- { at: 'app/_probe_mispaired.tsx', source: 'export const HELD = <Button.chip />\n' },
133
- ],
134
- at: 'app/_probe_mispaired.tsx',
135
- },
136
- ],
137
- right: [
138
- {
139
- files: [
140
- {
141
- at: 'threads/_probe_worn.ts',
142
- source:
143
- "export const ELEMENTS: Threaded = {\n id: 'element',\n declares: [\n {\n id: 'button',\n at: 'app/_probe_button.tsx',\n },\n ],\n}\n\nexport const VARIANTS: Threaded = {\n id: 'variant',\n declares: [\n {\n id: 'button-primary',\n at: 'app/_probe_variant.ts',\n wears: ['Button'],\n },\n ],\n}\n",
144
- },
145
- { at: 'app/_probe_declared.tsx', source: 'export const HELD = <Button.buttonPrimary />\n' },
146
- ],
147
- at: 'app/_probe_declared.tsx',
148
- },
149
- '<Unknown.anything />',
150
- ],
151
- },
152
- }
@@ -1,108 +0,0 @@
1
- import ts from 'typescript'
2
-
3
- import { descend } from '../../core/descend.ts'
4
-
5
- import type { Slopnator } from '../slopnator.ts'
6
-
7
- const said = (held: ts.Expression | undefined): string | undefined =>
8
- held !== undefined && ts.isStringLiteral(held) ? held.text : undefined
9
-
10
- const listed = (held: ts.Expression | undefined): readonly string[] =>
11
- held !== undefined && ts.isArrayLiteralExpression(held)
12
- ? held.elements.filter((one) => ts.isStringLiteral(one)).map((one) => (one as ts.StringLiteral).text)
13
- : []
14
-
15
- const yes = (held: ts.Expression | undefined): boolean =>
16
- held !== undefined && held.kind === ts.SyntaxKind.TrueKeyword
17
-
18
- interface Asking {
19
- readonly id: string
20
- readonly invokes: string
21
- readonly carries: boolean
22
- }
23
-
24
- function invoking(text: string): { readonly asking: readonly Asking[]; readonly rows: ReadonlyMap<string, ts.ObjectLiteralExpression> } {
25
- const file = ts.createSourceFile('row.ts', text, ts.ScriptTarget.Latest, true)
26
- const rows = new Map<string, ts.ObjectLiteralExpression>()
27
- const found: Asking[] = []
28
- const visit = (node: ts.Node): void => {
29
- if (ts.isObjectLiteralExpression(node)) {
30
- const of = (name: string): ts.Expression | undefined => {
31
- for (const one of node.properties) {
32
- if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name) return one.initializer
33
- }
34
- return undefined
35
- }
36
- const id = said(of('id'))
37
- if (id !== undefined) rows.set(id, node)
38
- const invokes = said(of('invokes'))
39
- if (id !== undefined && invokes !== undefined) {
40
- found.push({ id, invokes, carries: said(of('arrives')) !== undefined && listed(of('requires')).length > 0 })
41
- }
42
- }
43
- ts.forEachChild(node, visit)
44
- }
45
- visit(file)
46
- return { asking: found, rows }
47
- }
48
-
49
- const fieldOf = (node: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined => {
50
- for (const one of node.properties) {
51
- if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name) return one.initializer
52
- }
53
- return undefined
54
- }
55
-
56
- export const slopnator: Slopnator = {
57
- holds: (_node, at) => {
58
- const { asking, rows } = invoking(at.text)
59
- if (asking.length === 0) return false
60
- const found: string[] = []
61
- const pass = descend()
62
- for (const one of asking) {
63
- const here = rows.get(one.invokes)
64
- const named = here === undefined ? undefined : listed(fieldOf(here, 'webhooks'))
65
- const signs = here === undefined ? undefined : yes(fieldOf(here, 'signed'))
66
- const runs = pass.stated.get(one.invokes)
67
- if (here === undefined && runs === undefined) {
68
- found.push(one.id + ' invokes ' + one.invokes + ', and nothing in this tree declares an operation by that name')
69
- continue
70
- }
71
- if (!(named ?? listed(runs?.fields.get('webhooks'))).includes(one.id)) {
72
- found.push(
73
- one.id + ' invokes ' + one.invokes + ', and that operation names no webhook ' + one.id +
74
- ' back, so it is reachable from outside by something it never agreed to',
75
- )
76
- }
77
- if ((signs ?? yes(runs?.fields.get('signed'))) && !one.carries) {
78
- found.push(
79
- one.invokes + ' takes only a signed webhook, and ' + one.id +
80
- ' states no header a signature arrives on and no secret to check it against',
81
- )
82
- }
83
- }
84
- return found.length === 0 ? false : found.join('; ')
85
- },
86
- proves: {
87
- wrong: [
88
- {
89
- files: [
90
- { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly invokes?: string\n readonly webhooks?: readonly string[]\n readonly declares?: readonly Threaded[]\n}\n" },
91
- { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n { at: 'threads/_probe_webhook.ts' },\n ],\n}\n" },
92
- { at: 'threads/_probe_webhook.ts', source: "export const WEBHOOKS: Threaded = {\n id: 'webhooks',\n declares: [\n { id: 'wh_stripe', at: 'app/inbound/wh_stripe/route.ts', invokes: 'wf_charge' },\n { id: 'wf_charge', at: 'operations/wf_charge.ts' },\n ],\n}\n" },
93
- ],
94
- at: 'threads/_probe_webhook.ts',
95
- },
96
- ],
97
- right: [
98
- {
99
- files: [
100
- { at: 'thread/core/thread.ts', source: "export interface Threaded {\n readonly id?: string\n readonly at?: string\n readonly describes?: string\n readonly version?: number\n readonly invokes?: string\n readonly webhooks?: readonly string[]\n readonly by?: string\n readonly runs?: string\n readonly declares?: readonly Threaded[]\n readonly derives?: readonly Threaded[]\n}\n" },
101
- { at: 'threads/thread.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const THREAD: Threaded = {\n declares: [\n { at: 'threads/_probe_webhook.ts' },\n ],\n}\n" },
102
- { at: 'threads/_probe_webhook.ts', source: "import type { Threaded } from '../thread/core/thread.ts'\n\nexport const WEBHOOKS: Threaded = {\n id: 'webhooks',\n version: 1,\n declares: [\n { id: 'wf_charge', at: 'operations/wf_charge.ts', webhooks: ['wh_stripe'] },\n ],\n derives: [\n { id: 'wh_stripe', at: 'app/inbound/wh_stripe/route.ts', invokes: 'wf_charge', by: 'thread capabilities', runs: 'thread capabilities --check' },\n ],\n}\n" },
103
- ],
104
- at: 'threads/_probe_webhook.ts',
105
- },
106
- ],
107
- },
108
- }
@@ -1,72 +0,0 @@
1
- import ts from 'typescript'
2
-
3
- import { LISTS as LINEAGE } from '../../core/descend.ts'
4
-
5
- import type { Slopnator } from '../slopnator.ts'
6
-
7
- const LISTS: ReadonlySet<string> = new Set(LINEAGE)
8
-
9
- const ROUTE = '/route.ts'
10
-
11
- const said = (held: ts.Expression | undefined): string | undefined =>
12
- held !== undefined && ts.isStringLiteral(held) ? held.text : undefined
13
-
14
- const fieldOf = (node: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined => {
15
- for (const one of node.properties) {
16
- if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name) return one.initializer
17
- }
18
- return undefined
19
- }
20
-
21
- export const slopnator: Slopnator = {
22
- holds: (_node, at) => {
23
- const file = ts.createSourceFile('row.ts', at.text, ts.ScriptTarget.Latest, true)
24
- const routes = new Map<string, string>()
25
- const permitted = new Set<string>()
26
-
27
- const visit = (node: ts.Node): void => {
28
- if (
29
- ts.isPropertyAssignment(node) &&
30
- ts.isIdentifier(node.name) &&
31
- LISTS.has(node.name.text) &&
32
- ts.isArrayLiteralExpression(node.initializer)
33
- ) {
34
- const list = node.name.text
35
- for (const one of node.initializer.elements) {
36
- if (!ts.isObjectLiteralExpression(one)) continue
37
- const where = said(fieldOf(one, 'at'))
38
- if (where === undefined || !where.endsWith(ROUTE)) continue
39
- routes.set(where, list)
40
- if (list === 'derives' && said(fieldOf(one, 'by')) !== undefined && said(fieldOf(one, 'runs')) !== undefined) {
41
- permitted.add(where)
42
- }
43
- }
44
- }
45
- ts.forEachChild(node, visit)
46
- }
47
- visit(file)
48
-
49
- const found: string[] = []
50
- for (const [where, list] of routes) {
51
- if (permitted.has(where)) continue
52
- found.push(
53
- where + ' is named by a ' + list + ' row' +
54
- (list === 'derives' ? ' that states no by and no runs' : '') +
55
- ', and a route is only ever derived',
56
- )
57
- }
58
- return found.length === 0 ? false : found.join('; ')
59
- },
60
- proves: {
61
- wrong: ["const M = { seeds: [{ at: 'app/inbound/wh_stripe/route.ts' }] }"],
62
- right: [
63
- {
64
- files: [
65
- { at: 'threads/thread.ts', source: "export const THREAD = {\n declares: [\n { at: 'threads/_probe_route.ts' },\n ],\n}\n" },
66
- { at: 'threads/_probe_route.ts', source: "export const M = {\n derives: [\n { at: 'app/inbound/wh_stripe/route.ts', by: 'thread capabilities', runs: 'thread capabilities --check' },\n ],\n}\n" },
67
- ],
68
- at: 'threads/_probe_route.ts',
69
- },
70
- ],
71
- },
72
- }
@@ -1,65 +0,0 @@
1
- import type { Slopnator } from '../slopnator.ts'
2
-
3
- const COMMON = new Set([
4
- 'a', 'an', 'the', 'of', 'for', 'to', 'and', 'or', 'is', 'it', 'its', 'this', 'that',
5
- 'in', 'on', 'at', 'as', 'by', 'with', 'be', 'are', 'was', 'one', 'every', 'each',
6
- ])
7
-
8
- type Node = {
9
- type?: string
10
- name?: string
11
- key?: { type?: string; name?: string; value?: unknown }
12
- value?: { type?: string; value?: unknown }
13
- computed?: boolean
14
- properties?: readonly Node[]
15
- }
16
-
17
- const keyOf = (property: Node): string | undefined => {
18
- if (property.computed === true) return undefined
19
- const key = property.key
20
- if (key?.type === 'Identifier') return key.name
21
- if (key?.type === 'Literal' && typeof key.value === 'string') return key.value
22
- return undefined
23
- }
24
-
25
- const said = (property: Node | undefined): string | undefined => {
26
- const held = property?.value
27
- return held?.type === 'Literal' && typeof held.value === 'string' ? held.value : undefined
28
- }
29
-
30
- const words = (text: string): readonly string[] =>
31
- text.toLowerCase().split(/[^a-z]+/).filter((one) => one !== '' && !COMMON.has(one))
32
-
33
- export const slopnator: Slopnator = {
34
- on: ['Property'],
35
- holds: (node, at) => {
36
- const property = node as Node
37
- const key = keyOf(property)
38
- if (key !== 'describes' && key !== 'means') return false
39
-
40
- const sentence = said(property)
41
- if (sentence === undefined) return false
42
- if (sentence.trim() === '') return `\`${key}\` is empty, so the word arrived and the meaning never did`
43
-
44
- const owner = (at.ancestors() as readonly Node[]).at(-1)
45
- const id = said((owner?.properties ?? []).find((one) => keyOf(one) === 'id' || keyOf(one) === 'name'))
46
- if (id === undefined) return false
47
-
48
- const own = new Set(words(id))
49
- if (words(sentence).some((word) => !own.has(word))) return false
50
- return `\`${key}\` says "${sentence}", which adds no word its id \`${id}\` does not already carry`
51
- },
52
- proves: {
53
- wrong: [
54
- "const M = { id: 'law', describes: 'The law.' }",
55
- "const M = { id: 'law', describes: '' }",
56
- "const M = { id: 'term', means: '' }",
57
- "const M = { id: 'thread-check', describes: 'The thread check.' }",
58
- ],
59
- right: [
60
- "const M = { id: 'law', describes: 'A rule that must be guaranteed to be enforced.' }",
61
- "const M = { id: 'term', means: 'A word this repository coins, and what somebody reading it should understand.' }",
62
- "const M = { id: 'law', at: 'packages/core/integrity/src/constraint.ts' }",
63
- ],
64
- },
65
- }
@@ -1,60 +0,0 @@
1
- import type { Slopnator } from '../slopnator.ts'
2
- import { here, islandsDeclared } from './sl_unthreaded.ts'
3
- import { findTree } from '../../core/tree.ts'
4
-
5
- const TREE = `${findTree().name}/`
6
- const GOVERNS = /^\s*governs:\s*'([^']+)'/gm
7
-
8
- export const slopnator: Slopnator = {
9
- holds: (_node, at) => {
10
- const path = here(at.filename)
11
- if (!path.startsWith(TREE)) return false
12
-
13
- let islands: readonly string[]
14
- try {
15
- islands = islandsDeclared().map((one) => one.id)
16
- } catch {
17
- return 'this file cannot be parsed, so the islands the tree declares were never read and nothing a law threaded to govern was measured'
18
- }
19
-
20
- for (const match of at.text.matchAll(GOVERNS)) {
21
- const governed = match[1] ?? ''
22
- if (islands.includes(governed)) continue
23
- return `a law governs "${governed}" and the tree declares no island by that name, so the rule reads as coverage of a kind nobody can declare`
24
- }
25
-
26
- return false
27
- },
28
- proves: {
29
- wrong: [
30
- {
31
- files: [
32
- {
33
- at: 'threads/_governed_probe.ts',
34
- source: "export const L: Constraint = {\n id: 'a-law',\n governs: 'no-island-by-this-name',\n}\n",
35
- },
36
- ],
37
- at: 'threads/_governed_probe.ts',
38
- },
39
- ],
40
- right: [
41
- {
42
- files: [
43
- { at: 'threads/thread.ts', source: "export const THREAD: Threaded = {\n declares: [\n { id: 'entities', at: 'src/islands/entities.ts', governs: ['app/_declare/entities.ts'] },\n ],\n}\n" },
44
- { at: 'src/islands/entities.ts', source: 'export const EntitiesIslandSchema = {}\nexport function defineEntities() {}\n' },
45
- { at: 'threads/_governed_probe.ts', source: "export const L: Constraint = {\n id: 'a-law',\n governs: 'entities',\n}\n" },
46
- ],
47
- at: 'threads/_governed_probe.ts',
48
- },
49
- {
50
- files: [
51
- {
52
- at: 'threads/_governed_probe.ts',
53
- source: "export const M: Islanding = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['operations/'] },\n ],\n}\n",
54
- },
55
- ],
56
- at: 'threads/_governed_probe.ts',
57
- },
58
- ],
59
- },
60
- }
@@ -1,46 +0,0 @@
1
- import { ROOT } from '../../core/descend.ts'
2
- import { valuesIn } from '../../core/rows.ts'
3
- import { kind } from '../../integrations/disk.ts'
4
- import { REACH, programOf, unlisted } from '../../integrations/target.ts'
5
-
6
- import type { Slopnator } from '../slopnator.ts'
7
-
8
- const TARGETS = 'TARGETS'
9
-
10
- const there = (path: string): boolean => kind(path) === 'file'
11
-
12
- export const slopnator: Slopnator = {
13
- holds: (_node, at) => {
14
- let held: { readonly TARGETS?: readonly { readonly id?: unknown }[] }
15
- try {
16
- held = valuesIn(at.text) as { readonly TARGETS?: readonly { readonly id?: unknown }[] }
17
- } catch (thrown) {
18
- if (!at.text.includes(TARGETS)) return false
19
- return 'this sheet names ' + TARGETS + ' and could not be read as values, so no target on it was checked — ' + (thrown instanceof Error ? thrown.message : String(thrown))
20
- }
21
- const found: string[] = []
22
- for (const one of held.TARGETS ?? []) {
23
- if (typeof one.id !== 'string' || !one.id.startsWith(REACH.cli)) continue
24
- const name = one.id.slice(REACH.cli.length)
25
- if (programOf(name, ROOT, there) === undefined) found.push(unlisted(name))
26
- }
27
- return found.length === 0 ? false : found.join('. ')
28
- },
29
- proves: {
30
- wrong: [
31
- "export const TARGETS: readonly Target[] = [{ id: 'cli_bash', of: 'in_shell', describes: 'The shell on the machine a command runs on.', version: 1, takes: [], requires: [] }]",
32
- ],
33
- right: [
34
- {
35
- files: [
36
- { at: 'node_modules/.bin/tsc', source: '' },
37
- { at: 'threads/_probe_targets.ts', source: "export const TARGETS: readonly Target[] = [{ id: 'cli_tsc', of: 'in_typescript', describes: 'The compiler this tree is checked and built with.', version: 1, takes: [], requires: [] }]\n" },
38
- ],
39
- at: 'threads/_probe_targets.ts',
40
- },
41
- "export const TARGETS: readonly Target[] = [{ id: 'cli_packager', of: 'in_packager', describes: 'The package manager the lockfile beside this tree picks.', version: 1, takes: [], requires: [] }]",
42
- "export const TARGETS: readonly Target[] = [{ id: 'cli_node', of: 'in_node', describes: 'The runtime this framework runs on.', version: 1, takes: [], requires: [] }]",
43
- "export const TARGETS: readonly Target[] = [{ id: 'ht_registry', of: 'in_registry', describes: 'The registry every package goes to.', version: 1, takes: [], requires: [], address: 'https://registry.npmjs.org' }]",
44
- ],
45
- },
46
- }
@@ -1,41 +0,0 @@
1
- import type { Slopnator } from '../slopnator.ts'
2
- import { declaresIsland, here, islandsDeclared } from './sl_unthreaded.ts'
3
-
4
- export const slopnator: Slopnator = {
5
- holds: (_node, at) => {
6
- const schema = declaresIsland(at.text)
7
- if (schema === undefined) return false
8
-
9
- const path = here(at.filename)
10
- if (islandsDeclared().some((island) => island.at === path)) return false
11
-
12
- return (
13
- `${schema}IslandSchema is an island and no row in the tree names it, so nothing states what it governs and ` +
14
- 'no law can reach it — the kind is declarable and every rule about where it may be declared is absent'
15
- )
16
- },
17
- proves: {
18
- wrong: [
19
- {
20
- files: [
21
- {
22
- at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
23
- source: 'export const ThingIslandSchema = z.strictObject({\n specVersion: z.literal(1),\n})\n',
24
- },
25
- ],
26
- at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
27
- },
28
- ],
29
- right: [
30
- {
31
- files: [
32
- {
33
- at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
34
- source: 'export const ThingSchema = z.strictObject({\n name: z.string(),\n})\n',
35
- },
36
- ],
37
- at: 'packages/core/spec/src/islands/_unnamed_probe.ts',
38
- },
39
- ],
40
- },
41
- }