@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.
- package/CHANGELOG.md +109 -0
- package/dist/core/attach.js +21 -0
- package/dist/core/descend.js +6 -3
- package/dist/core/generators/commit.js +55 -3
- package/dist/core/generators/dev.d.ts +1 -0
- package/dist/core/generators/dev.js +68 -0
- package/dist/core/tree.d.ts +1 -0
- package/dist/core/tree.js +7 -1
- package/dist/derivation/generators/build.d.ts +3 -1
- package/dist/derivation/generators/build.js +103 -22
- package/dist/derivation/generators/release.js +74 -26
- package/dist/derivation/release.js +3 -3
- package/dist/integrations/cli.js +69 -10
- package/dist/integrations/pglite.js +13 -3
- package/dist/integrations/target.d.ts +3 -0
- package/dist/integrity/attach.js +1 -5
- package/dist/integrity/slopnators/sl_stale_path.js +10 -0
- package/dist/operations/access.js +3 -2
- package/dist/operations/capabilities.d.ts +2 -0
- package/dist/operations/capabilities.js +2 -0
- package/dist/operations/generators/capability.js +234 -24
- package/dist/operations/generators/compile.js +150 -50
- package/dist/operations/generators/seed.d.ts +3 -0
- package/dist/operations/generators/seed.js +25 -3
- package/dist/operations/nodes.d.ts +2 -2
- package/package.json +1 -1
- package/dist/core/actor.ts +0 -21
- package/dist/core/attach.ts +0 -613
- package/dist/core/chain.ts +0 -145
- package/dist/core/condition.ts +0 -227
- package/dist/core/descend.ts +0 -1185
- package/dist/core/errors/catalogue.ts +0 -314
- package/dist/core/errors/classes.ts +0 -51
- package/dist/core/errors/error.ts +0 -152
- package/dist/core/errors/namespaces.ts +0 -19
- package/dist/core/finding.ts +0 -37
- package/dist/core/generators/commit.ts +0 -157
- package/dist/core/init.ts +0 -222
- package/dist/core/invoked.ts +0 -52
- package/dist/core/round.ts +0 -74
- package/dist/core/rows.ts +0 -506
- package/dist/core/scope.ts +0 -20
- package/dist/core/thread.ts +0 -29
- package/dist/core/tree.ts +0 -90
- package/dist/core/types.ts +0 -351
- package/dist/derivation/attach.ts +0 -57
- package/dist/derivation/generators/build.ts +0 -371
- package/dist/derivation/generators/check.ts +0 -118
- package/dist/derivation/generators/determinism.ts +0 -145
- package/dist/derivation/generators/release.ts +0 -236
- package/dist/derivation/release.ts +0 -43
- package/dist/integrations/attach.ts +0 -187
- package/dist/integrations/cli.ts +0 -606
- package/dist/integrations/declared.ts +0 -34
- package/dist/integrations/disk.ts +0 -466
- package/dist/integrations/generators/read.ts +0 -127
- package/dist/integrations/generators/vault.ts +0 -210
- package/dist/integrations/https.ts +0 -216
- package/dist/integrations/pglite.ts +0 -206
- package/dist/integrations/secret.ts +0 -54
- package/dist/integrations/target.ts +0 -672
- package/dist/integrations/vault.ts +0 -148
- package/dist/integrity/attach.ts +0 -150
- package/dist/integrity/check-threads.ts +0 -166
- package/dist/integrity/constraint.ts +0 -1246
- package/dist/integrity/generators/compiler.ts +0 -138
- package/dist/integrity/generators/gate.ts +0 -227
- package/dist/integrity/generators/issues.ts +0 -160
- package/dist/integrity/generators/publish.ts +0 -233
- package/dist/integrity/generators/trial.ts +0 -359
- package/dist/integrity/lint.ts +0 -214
- package/dist/integrity/lsp/bin.ts +0 -44
- package/dist/integrity/lsp/diagnostics.ts +0 -92
- package/dist/integrity/lsp/findings.ts +0 -63
- package/dist/integrity/lsp/lint.ts +0 -124
- package/dist/integrity/lsp/protocol.ts +0 -78
- package/dist/integrity/lsp/server.ts +0 -224
- package/dist/integrity/slopnator.ts +0 -324
- package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
- package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
- package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
- package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
- package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
- package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
- package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
- package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
- package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
- package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
- package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
- package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
- package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
- package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
- package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
- package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
- package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
- package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
- package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
- package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
- package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
- package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
- package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
- package/dist/integrity/sweep.ts +0 -395
- package/dist/knowledge/attach.ts +0 -327
- package/dist/knowledge/generators/docsgen.ts +0 -186
- package/dist/knowledge/generators/glossary.ts +0 -73
- package/dist/knowledge/glossary.ts +0 -6
- package/dist/operations/access.ts +0 -131
- package/dist/operations/attach.ts +0 -408
- package/dist/operations/capabilities.ts +0 -88
- package/dist/operations/dispatch.ts +0 -294
- package/dist/operations/error-results.ts +0 -31
- package/dist/operations/generators/capability.ts +0 -1054
- package/dist/operations/generators/compile.ts +0 -701
- package/dist/operations/generators/permits.ts +0 -229
- package/dist/operations/generators/seed.ts +0 -359
- package/dist/operations/generators/trial.ts +0 -666
- package/dist/operations/nodes.ts +0 -53
- package/dist/operations/operations.ts +0 -91
- 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
|
-
}
|