@prestack/thread 0.1.21 → 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 (137) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/dist/core/attach.js +21 -0
  3. package/dist/core/descend.js +6 -3
  4. package/dist/core/generators/commit.js +55 -3
  5. package/dist/core/generators/dev.d.ts +1 -0
  6. package/dist/core/generators/dev.js +68 -0
  7. package/dist/core/tree.d.ts +1 -0
  8. package/dist/core/tree.js +7 -1
  9. package/dist/derivation/generators/build.d.ts +3 -1
  10. package/dist/derivation/generators/build.js +103 -22
  11. package/dist/derivation/generators/release.js +74 -26
  12. package/dist/derivation/release.js +3 -3
  13. package/dist/integrations/cli.js +69 -10
  14. package/dist/integrations/pglite.js +13 -3
  15. package/dist/integrations/target.d.ts +3 -0
  16. package/dist/integrity/attach.js +1 -5
  17. package/dist/integrity/slopnators/sl_stale_path.js +10 -0
  18. package/dist/operations/access.js +3 -2
  19. package/dist/operations/capabilities.d.ts +2 -0
  20. package/dist/operations/capabilities.js +2 -0
  21. package/dist/operations/generators/capability.js +234 -24
  22. package/dist/operations/generators/compile.js +150 -50
  23. package/dist/operations/generators/seed.d.ts +3 -0
  24. package/dist/operations/generators/seed.js +25 -3
  25. package/dist/operations/nodes.d.ts +2 -2
  26. package/package.json +1 -1
  27. package/dist/core/actor.ts +0 -21
  28. package/dist/core/attach.ts +0 -613
  29. package/dist/core/chain.ts +0 -145
  30. package/dist/core/condition.ts +0 -227
  31. package/dist/core/descend.ts +0 -1185
  32. package/dist/core/errors/catalogue.ts +0 -314
  33. package/dist/core/errors/classes.ts +0 -51
  34. package/dist/core/errors/error.ts +0 -152
  35. package/dist/core/errors/namespaces.ts +0 -19
  36. package/dist/core/finding.ts +0 -37
  37. package/dist/core/generators/commit.ts +0 -157
  38. package/dist/core/init.ts +0 -222
  39. package/dist/core/invoked.ts +0 -52
  40. package/dist/core/round.ts +0 -74
  41. package/dist/core/rows.ts +0 -506
  42. package/dist/core/scope.ts +0 -20
  43. package/dist/core/thread.ts +0 -29
  44. package/dist/core/tree.ts +0 -90
  45. package/dist/core/types.ts +0 -351
  46. package/dist/derivation/attach.ts +0 -57
  47. package/dist/derivation/generators/build.ts +0 -371
  48. package/dist/derivation/generators/check.ts +0 -118
  49. package/dist/derivation/generators/determinism.ts +0 -145
  50. package/dist/derivation/generators/release.ts +0 -236
  51. package/dist/derivation/release.ts +0 -43
  52. package/dist/integrations/attach.ts +0 -187
  53. package/dist/integrations/cli.ts +0 -606
  54. package/dist/integrations/declared.ts +0 -34
  55. package/dist/integrations/disk.ts +0 -466
  56. package/dist/integrations/generators/read.ts +0 -127
  57. package/dist/integrations/generators/vault.ts +0 -210
  58. package/dist/integrations/https.ts +0 -216
  59. package/dist/integrations/pglite.ts +0 -206
  60. package/dist/integrations/secret.ts +0 -54
  61. package/dist/integrations/target.ts +0 -672
  62. package/dist/integrations/vault.ts +0 -148
  63. package/dist/integrity/attach.ts +0 -150
  64. package/dist/integrity/check-threads.ts +0 -166
  65. package/dist/integrity/constraint.ts +0 -1246
  66. package/dist/integrity/generators/compiler.ts +0 -138
  67. package/dist/integrity/generators/gate.ts +0 -227
  68. package/dist/integrity/generators/issues.ts +0 -160
  69. package/dist/integrity/generators/publish.ts +0 -233
  70. package/dist/integrity/generators/trial.ts +0 -359
  71. package/dist/integrity/lint.ts +0 -214
  72. package/dist/integrity/lsp/bin.ts +0 -44
  73. package/dist/integrity/lsp/diagnostics.ts +0 -92
  74. package/dist/integrity/lsp/findings.ts +0 -63
  75. package/dist/integrity/lsp/lint.ts +0 -124
  76. package/dist/integrity/lsp/protocol.ts +0 -78
  77. package/dist/integrity/lsp/server.ts +0 -224
  78. package/dist/integrity/slopnator.ts +0 -324
  79. package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
  80. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
  81. package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
  82. package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
  83. package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
  84. package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
  85. package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
  86. package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
  87. package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
  88. package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
  89. package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
  90. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
  91. package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
  92. package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
  93. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
  94. package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
  95. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
  96. package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
  97. package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
  98. package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
  99. package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
  100. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
  101. package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
  102. package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
  103. package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
  104. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
  105. package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
  106. package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
  107. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
  108. package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
  109. package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
  110. package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
  111. package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
  112. package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
  113. package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
  114. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
  115. package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
  116. package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
  117. package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
  118. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
  119. package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
  120. package/dist/integrity/sweep.ts +0 -395
  121. package/dist/knowledge/attach.ts +0 -327
  122. package/dist/knowledge/generators/docsgen.ts +0 -186
  123. package/dist/knowledge/generators/glossary.ts +0 -73
  124. package/dist/knowledge/glossary.ts +0 -6
  125. package/dist/operations/access.ts +0 -131
  126. package/dist/operations/attach.ts +0 -408
  127. package/dist/operations/capabilities.ts +0 -88
  128. package/dist/operations/dispatch.ts +0 -294
  129. package/dist/operations/error-results.ts +0 -31
  130. package/dist/operations/generators/capability.ts +0 -1054
  131. package/dist/operations/generators/compile.ts +0 -701
  132. package/dist/operations/generators/permits.ts +0 -229
  133. package/dist/operations/generators/seed.ts +0 -359
  134. package/dist/operations/generators/trial.ts +0 -666
  135. package/dist/operations/nodes.ts +0 -53
  136. package/dist/operations/operations.ts +0 -91
  137. 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, readFileSync } 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, (path) => readFileSync(path, 'utf8')) === 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
- }