@prestack/thread 0.1.3 → 0.1.5
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 +563 -0
- package/dist/core/actor.d.ts +1 -1
- package/dist/core/actor.js +5 -4
- package/dist/core/actor.ts +5 -4
- package/dist/core/attach.d.ts +0 -7
- package/dist/core/attach.js +38 -67
- package/dist/core/attach.ts +34 -80
- package/dist/core/chain.js +13 -17
- package/dist/core/chain.ts +11 -15
- package/dist/core/condition.d.ts +4 -4
- package/dist/core/condition.js +2 -2
- package/dist/core/descend.d.ts +4 -2
- package/dist/core/descend.js +91 -23
- package/dist/core/descend.ts +90 -21
- package/dist/core/errors/catalogue.d.ts +52 -0
- package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
- package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
- package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
- package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
- package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
- package/dist/core/errors/namespaces.d.ts +3 -0
- package/dist/core/errors/namespaces.js +15 -0
- package/dist/core/errors/namespaces.ts +19 -0
- package/dist/core/finding.js +2 -2
- package/dist/core/finding.ts +1 -2
- package/dist/core/init.d.ts +1 -1
- package/dist/core/init.js +51 -5
- package/dist/core/init.ts +58 -5
- package/dist/core/invoked.d.ts +1 -1
- package/dist/core/invoked.js +3 -1
- package/dist/core/invoked.ts +5 -2
- package/dist/core/round.js +36 -22
- package/dist/core/round.ts +26 -9
- package/dist/core/rows.d.ts +4 -0
- package/dist/core/rows.js +31 -2
- package/dist/core/rows.ts +33 -1
- package/dist/core/scope.d.ts +1 -0
- package/dist/core/scope.js +1 -0
- package/dist/core/scope.ts +2 -0
- package/dist/core/thread.d.ts +3 -3
- package/dist/core/tree.d.ts +4 -1
- package/dist/core/tree.js +26 -6
- package/dist/core/tree.ts +29 -6
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +3 -1
- package/dist/core/types.ts +2 -1
- package/dist/derivation/attach.js +3 -2
- package/dist/derivation/attach.ts +2 -1
- package/dist/derivation/generators/build.js +6 -6
- package/dist/derivation/generators/build.ts +1 -1
- package/dist/derivation/generators/check.js +1 -1
- package/dist/derivation/generators/determinism.js +1 -1
- package/dist/derivation/generators/release.js +60 -47
- package/dist/derivation/generators/release.ts +66 -43
- package/dist/derivation/release.d.ts +7 -3
- package/dist/derivation/release.js +14 -1
- package/dist/derivation/release.ts +22 -2
- package/dist/integrations/attach.d.ts +4 -0
- package/dist/integrations/attach.js +37 -56
- package/dist/integrations/attach.ts +54 -60
- package/dist/integrations/cli.d.ts +37 -0
- package/dist/integrations/cli.js +440 -0
- package/dist/integrations/cli.ts +543 -0
- package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
- package/dist/integrations/disk.js +435 -0
- package/dist/integrations/disk.ts +465 -0
- package/dist/integrations/generators/read.js +6 -6
- package/dist/integrations/generators/read.ts +4 -4
- package/dist/integrations/generators/vault.js +10 -11
- package/dist/integrations/generators/vault.ts +4 -5
- package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
- package/dist/integrations/https.js +170 -0
- package/dist/integrations/https.ts +216 -0
- package/dist/integrations/pglite.d.ts +26 -0
- package/dist/integrations/pglite.js +143 -0
- package/dist/integrations/pglite.ts +206 -0
- package/dist/integrations/secret.d.ts +8 -2
- package/dist/integrations/secret.js +3 -5
- package/dist/integrations/secret.ts +10 -6
- package/dist/integrations/target.d.ts +155 -14
- package/dist/integrations/target.js +171 -50
- package/dist/integrations/target.ts +377 -61
- package/dist/integrations/vault.d.ts +1 -0
- package/dist/integrations/vault.js +20 -5
- package/dist/integrations/vault.ts +17 -5
- package/dist/integrity/attach.js +8 -77
- package/dist/integrity/attach.ts +7 -76
- package/dist/integrity/check-threads.js +5 -5
- package/dist/integrity/check-threads.ts +2 -2
- package/dist/integrity/constraint.d.ts +0 -1
- package/dist/integrity/constraint.js +267 -366
- package/dist/integrity/constraint.ts +267 -375
- package/dist/integrity/generators/compiler.js +14 -17
- package/dist/integrity/generators/compiler.ts +10 -15
- package/dist/integrity/generators/gate.js +15 -15
- package/dist/integrity/generators/gate.ts +10 -10
- package/dist/integrity/generators/issues.js +7 -7
- package/dist/integrity/generators/publish.js +39 -57
- package/dist/integrity/generators/publish.ts +44 -55
- package/dist/integrity/generators/trial.d.ts +3 -3
- package/dist/integrity/generators/trial.js +133 -165
- package/dist/integrity/generators/trial.ts +145 -169
- package/dist/integrity/lint.js +9 -13
- package/dist/integrity/lint.ts +7 -10
- package/dist/integrity/lsp/bin.js +5 -4
- package/dist/integrity/lsp/bin.ts +3 -1
- package/dist/integrity/lsp/diagnostics.js +3 -3
- package/dist/integrity/lsp/diagnostics.ts +1 -1
- package/dist/integrity/lsp/lint.js +19 -30
- package/dist/integrity/lsp/lint.ts +16 -32
- package/dist/integrity/lsp/server.js +3 -3
- package/dist/integrity/slopnator.d.ts +3 -13
- package/dist/integrity/slopnator.js +59 -38
- package/dist/integrity/slopnator.ts +54 -38
- package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
- package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
- package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
- package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
- package/dist/integrity/slopnators/sl_inert_island.js +10 -8
- package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
- package/dist/integrity/slopnators/sl_missing_row.js +6 -51
- package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
- package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
- package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
- package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
- package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
- package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
- package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
- package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
- package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
- package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
- package/dist/integrity/slopnators/sl_stale_path.js +5 -22
- package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
- package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
- package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
- package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
- package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
- package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
- package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
- package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
- package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
- package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
- package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
- package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
- package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
- package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
- package/dist/integrity/slopnators/sl_underived_route.js +61 -0
- package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
- package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
- package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
- package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
- package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
- package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
- package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
- package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
- package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
- package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
- package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
- package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
- package/dist/integrity/slopnators/sl_unversioned.js +6 -4
- package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
- package/dist/integrity/sweep.d.ts +1 -0
- package/dist/integrity/sweep.js +105 -42
- package/dist/integrity/sweep.ts +115 -42
- package/dist/knowledge/attach.js +4 -3
- package/dist/knowledge/attach.ts +3 -2
- package/dist/knowledge/generators/docsgen.js +8 -6
- package/dist/knowledge/generators/docsgen.ts +4 -2
- package/dist/knowledge/generators/glossary.js +4 -4
- package/dist/knowledge/generators/glossary.ts +1 -1
- package/dist/operations/access.d.ts +23 -0
- package/dist/operations/access.js +28 -1
- package/dist/operations/access.ts +43 -0
- package/dist/operations/attach.js +33 -113
- package/dist/operations/attach.ts +51 -117
- package/dist/operations/capabilities.d.ts +14 -5
- package/dist/operations/capabilities.js +13 -4
- package/dist/operations/capabilities.ts +27 -8
- package/dist/operations/dispatch.d.ts +8 -2
- package/dist/operations/dispatch.js +128 -185
- package/dist/operations/dispatch.ts +138 -191
- package/dist/operations/error-results.d.ts +1 -1
- package/dist/operations/error-results.js +2 -2
- package/dist/operations/error-results.ts +2 -2
- package/dist/operations/generators/capability.d.ts +11 -1
- package/dist/operations/generators/capability.js +481 -224
- package/dist/operations/generators/capability.ts +562 -233
- package/dist/operations/generators/compile.js +66 -39
- package/dist/operations/generators/compile.ts +78 -39
- package/dist/operations/generators/permits.js +11 -12
- package/dist/operations/generators/permits.ts +3 -4
- package/dist/operations/generators/seed.d.ts +1 -0
- package/dist/operations/generators/seed.js +19 -17
- package/dist/operations/generators/seed.ts +22 -14
- package/dist/operations/generators/trial.d.ts +6 -8
- package/dist/operations/generators/trial.js +200 -70
- package/dist/operations/generators/trial.ts +230 -93
- package/dist/operations/nodes.d.ts +15 -16
- package/dist/operations/nodes.ts +16 -17
- package/dist/operations/operations.d.ts +40 -4
- package/dist/operations/operations.js +1 -0
- package/dist/operations/operations.ts +61 -4
- package/dist/operations/result.d.ts +2 -2
- package/dist/operations/result.ts +2 -2
- package/package.json +1 -4
- package/dist/core/answers.d.ts +0 -171
- package/dist/core/answers.js +0 -789
- package/dist/core/answers.ts +0 -999
- package/dist/core/client.d.ts +0 -63
- package/dist/core/client.js +0 -249
- package/dist/core/client.ts +0 -289
- package/dist/core/files.js +0 -171
- package/dist/core/files.ts +0 -222
- package/dist/integrations/module.d.ts +0 -6
- package/dist/integrations/module.js +0 -153
- package/dist/integrations/module.ts +0 -191
- package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
- package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
- package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
- package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
- package/dist/operations/cost.d.ts +0 -14
- package/dist/operations/cost.js +0 -37
- package/dist/operations/cost.ts +0 -58
- package/dist/operations/errors/errors.d.ts +0 -52
- package/dist/operations/http.js +0 -124
- package/dist/operations/http.ts +0 -169
- package/dist/operations/memory.d.ts +0 -104
- package/dist/operations/memory.js +0 -365
- package/dist/operations/memory.ts +0 -470
- package/dist/operations/metering.d.ts +0 -55
- package/dist/operations/metering.js +0 -247
- package/dist/operations/metering.ts +0 -328
- package/dist/operations/queue-policy.d.ts +0 -3
- package/dist/operations/queue-policy.js +0 -8
- package/dist/operations/queue-policy.ts +0 -10
- package/dist/operations/seams.d.ts +0 -239
- package/dist/operations/seams.js +0 -45
- package/dist/operations/seams.ts +0 -364
- package/dist/operations/store.d.ts +0 -14
- package/dist/operations/store.js +0 -1
- package/dist/operations/store.ts +0 -29
- /package/dist/{operations → core}/errors/classes.d.ts +0 -0
- /package/dist/{operations → core}/errors/classes.js +0 -0
- /package/dist/{operations → core}/errors/classes.ts +0 -0
- /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
- /package/dist/integrity/slopnators/{sl_unseamed_reach.d.ts → sl_outside_an_operation.d.ts} +0 -0
|
@@ -21,14 +21,137 @@ function declared(): ReadonlySet<string> {
|
|
|
21
21
|
const where = one.at?.split('\\').join('/')
|
|
22
22
|
if (where === undefined || !where.includes(UNDER)) continue
|
|
23
23
|
if (one.id !== undefined) found.add(one.id)
|
|
24
|
-
const named = where.split('/').at(-1)?.replace(/\.[^.]+$/, '')
|
|
25
|
-
if (named !== undefined) found.add(named)
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
return found
|
|
30
28
|
}
|
|
31
29
|
|
|
30
|
+
function bodies(): ReadonlySet<string> {
|
|
31
|
+
const found = new Set<string>()
|
|
32
|
+
|
|
33
|
+
descend()
|
|
34
|
+
|
|
35
|
+
for (const at of reached()) {
|
|
36
|
+
const text = read(at)
|
|
37
|
+
if (text === undefined) continue
|
|
38
|
+
|
|
39
|
+
for (const one of rowsIn(text)) {
|
|
40
|
+
const where = one.at?.split('\\').join('/')
|
|
41
|
+
if (where === undefined || !where.includes(UNDER)) continue
|
|
42
|
+
if (one.id !== undefined) found.add(where.replace(/\.[^./]+$/, ''))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return found
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function declaring(): ReadonlySet<string> {
|
|
50
|
+
const found = new Set<string>()
|
|
51
|
+
|
|
52
|
+
descend()
|
|
53
|
+
|
|
54
|
+
for (const at of reached()) {
|
|
55
|
+
const text = read(at)
|
|
56
|
+
if (text === undefined) continue
|
|
57
|
+
|
|
58
|
+
for (const one of rowsIn(text)) {
|
|
59
|
+
const where = one.at?.split('\\').join('/')
|
|
60
|
+
if (where === undefined) continue
|
|
61
|
+
found.add(where.replace(/\.[^./]+$/, ''))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return found
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface Imported {
|
|
69
|
+
readonly type?: string
|
|
70
|
+
readonly source?: { readonly value?: unknown }
|
|
71
|
+
readonly specifiers?: readonly { readonly local?: { readonly name?: unknown } }[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function importedFrom(name: string, ancestors: readonly unknown[]): string | undefined {
|
|
75
|
+
const program = ancestors.find((one) => (one as { readonly type?: string } | undefined)?.type === 'Program') as
|
|
76
|
+
| { readonly body?: readonly Imported[] }
|
|
77
|
+
| undefined
|
|
78
|
+
for (const one of program?.body ?? []) {
|
|
79
|
+
if (one.type !== 'ImportDeclaration' || typeof one.source?.value !== 'string') continue
|
|
80
|
+
if (!(one.specifiers ?? []).some((each) => each.local?.name === name)) continue
|
|
81
|
+
return one.source.value
|
|
82
|
+
}
|
|
83
|
+
return undefined
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface Typed {
|
|
87
|
+
readonly typeName?: { readonly name?: unknown }
|
|
88
|
+
readonly typeAnnotation?: Typed
|
|
89
|
+
readonly types?: readonly Typed[]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface Annotated {
|
|
93
|
+
readonly type?: string
|
|
94
|
+
readonly name?: unknown
|
|
95
|
+
readonly typeAnnotation?: Typed
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface Held {
|
|
99
|
+
readonly type?: string
|
|
100
|
+
readonly params?: readonly Annotated[]
|
|
101
|
+
readonly body?: unknown
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function namedIn(said: Typed | undefined): string | undefined {
|
|
105
|
+
if (said === undefined) return undefined
|
|
106
|
+
if (typeof said.typeName?.name === 'string') return said.typeName.name
|
|
107
|
+
const inner = namedIn(said.typeAnnotation)
|
|
108
|
+
if (inner !== undefined) return inner
|
|
109
|
+
for (const one of said.types ?? []) {
|
|
110
|
+
const found = namedIn(one)
|
|
111
|
+
if (found !== undefined) return found
|
|
112
|
+
}
|
|
113
|
+
return undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const typeNamed = (held: Annotated | undefined): string | undefined => namedIn(held?.typeAnnotation?.typeAnnotation)
|
|
117
|
+
|
|
118
|
+
interface Stated {
|
|
119
|
+
readonly type?: string
|
|
120
|
+
readonly declarations?: readonly { readonly id?: Annotated }[]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function annotating(name: string, ancestors: readonly unknown[]): string | undefined {
|
|
124
|
+
for (const held of ancestors) {
|
|
125
|
+
for (const param of (held as Held).params ?? []) {
|
|
126
|
+
if (param.type !== 'Identifier' || param.name !== name) continue
|
|
127
|
+
const said = typeNamed(param)
|
|
128
|
+
if (said !== undefined) return said
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const body = (held as Held).body
|
|
132
|
+
for (const one of Array.isArray(body) ? (body as readonly Stated[]) : []) {
|
|
133
|
+
if (one.type !== 'VariableDeclaration') continue
|
|
134
|
+
for (const each of one.declarations ?? []) {
|
|
135
|
+
if (each.id?.type !== 'Identifier' || each.id.name !== name) continue
|
|
136
|
+
const said = typeNamed(each.id)
|
|
137
|
+
if (said !== undefined) return said
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const settled = (from: string, source: string): string => {
|
|
145
|
+
if (!source.startsWith('.')) return source.replace(/\.[^./]+$/, '')
|
|
146
|
+
const parts = from.split('/').slice(0, -1)
|
|
147
|
+
for (const step of source.replace(/\.[^./]+$/, '').split('/')) {
|
|
148
|
+
if (step === '.' || step === '') continue
|
|
149
|
+
if (step === '..') parts.pop()
|
|
150
|
+
else parts.push(step)
|
|
151
|
+
}
|
|
152
|
+
return parts.join('/')
|
|
153
|
+
}
|
|
154
|
+
|
|
32
155
|
interface Called {
|
|
33
156
|
readonly callee?: { readonly type?: string; readonly name?: string; readonly property?: { readonly type?: string; readonly name?: string } }
|
|
34
157
|
readonly arguments?: readonly { readonly type?: string; readonly value?: unknown; readonly name?: string }[]
|
|
@@ -36,7 +159,7 @@ interface Called {
|
|
|
36
159
|
|
|
37
160
|
export const slopnator: Slopnator = {
|
|
38
161
|
on: ['CallExpression'],
|
|
39
|
-
holds: (node) => {
|
|
162
|
+
holds: (node, at) => {
|
|
40
163
|
const call = node as Called | undefined
|
|
41
164
|
const callee = call?.callee
|
|
42
165
|
const named =
|
|
@@ -55,34 +178,19 @@ export const slopnator: Slopnator = {
|
|
|
55
178
|
)
|
|
56
179
|
}
|
|
57
180
|
|
|
58
|
-
/*
|
|
59
|
-
* THE OPERATION ITSELF IS A BETTER ANSWER THAN ITS NAME, AND THIS USED TO REFUSE IT.
|
|
60
|
-
*
|
|
61
|
-
* This asked for a string literal and refused everything else, which was right when the only
|
|
62
|
-
* caller was a UI seam handed a bare name. It has been wrong since dispatch stopped taking one:
|
|
63
|
-
* the note above says so in as many words — "`dispatch` in packages/core/operations/src/dispatch.ts
|
|
64
|
-
* takes an Operation object rather than a name". What ships now imports the compiled operation
|
|
65
|
-
* and hands over the binding, and a binding cannot be assembled at run time: if it named nothing,
|
|
66
|
-
* the file would not compile.
|
|
67
|
-
*
|
|
68
|
-
* Measured 2026-08-31 on a registry install: an operations-only app, everything made by the CLI,
|
|
69
|
-
* failed its own sweep on `operations/_client.ts` — a file its own generator had just written,
|
|
70
|
-
* naming an operation the same tree declares. The gate refuses the build now, so a law that
|
|
71
|
-
* refuses a generated file is an app that cannot ship.
|
|
72
|
-
*
|
|
73
|
-
* Narrow on purpose. An identifier is accepted only when it IS one of the declared operations,
|
|
74
|
-
* which is the shape the generators emit — `import { operation as wf_charge }` and then
|
|
75
|
-
* `dispatch(wf_charge, …)`. A variable holding a computed name is still refused, because it does
|
|
76
|
-
* not match a declaration, and that is the hole this law was written for.
|
|
77
|
-
*/
|
|
78
181
|
const all = declared()
|
|
79
182
|
|
|
80
183
|
if (first.type === 'Identifier' && typeof first.name === 'string') {
|
|
81
184
|
if (all.has(first.name)) return false
|
|
185
|
+
const source = importedFrom(first.name, at.ancestors())
|
|
186
|
+
if (source !== undefined && bodies().has(settled(at.filename, source))) return false
|
|
187
|
+
const named = annotating(first.name, at.ancestors())
|
|
188
|
+
const typed = named === undefined ? undefined : importedFrom(named, at.ancestors())
|
|
189
|
+
if (typed !== undefined && declaring().has(settled(at.filename, typed))) return false
|
|
82
190
|
return (
|
|
83
|
-
`dispatch is handed \`${first.name}\`, and
|
|
84
|
-
'checkable when it is the operation — import the compiled operation and hand that over, so
|
|
85
|
-
'what was declared are the same thing rather than two things that agree today'
|
|
191
|
+
`dispatch is handed \`${first.name}\`, and it is not imported from a file any row declares as an operation. ` +
|
|
192
|
+
'A binding is only checkable when it is the operation — import the compiled operation and hand that over, so ' +
|
|
193
|
+
'what runs and what was declared are the same thing rather than two things that agree today'
|
|
86
194
|
)
|
|
87
195
|
}
|
|
88
196
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { descend, read, reached } from
|
|
2
|
-
import { rowsIn } from
|
|
1
|
+
import { descend, read, reached } from '../../core/descend.js';
|
|
2
|
+
import { rowsIn } from './sl_unthreaded.js';
|
|
3
3
|
const ELEMENT = 'element';
|
|
4
4
|
const VARIANT = 'variant';
|
|
5
5
|
function nameOf(id) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { descend } from '../../core/descend.js';
|
|
3
|
+
const said = (held) => held !== undefined && ts.isStringLiteral(held) ? held.text : undefined;
|
|
4
|
+
const listed = (held) => held !== undefined && ts.isArrayLiteralExpression(held)
|
|
5
|
+
? held.elements.filter((one) => ts.isStringLiteral(one)).map((one) => one.text)
|
|
6
|
+
: [];
|
|
7
|
+
const yes = (held) => held !== undefined && held.kind === ts.SyntaxKind.TrueKeyword;
|
|
8
|
+
function invoking(text) {
|
|
9
|
+
const file = ts.createSourceFile('row.ts', text, ts.ScriptTarget.Latest, true);
|
|
10
|
+
const rows = new Map();
|
|
11
|
+
const found = [];
|
|
12
|
+
const visit = (node) => {
|
|
13
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
14
|
+
const of = (name) => {
|
|
15
|
+
for (const one of node.properties) {
|
|
16
|
+
if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name)
|
|
17
|
+
return one.initializer;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
};
|
|
21
|
+
const id = said(of('id'));
|
|
22
|
+
if (id !== undefined)
|
|
23
|
+
rows.set(id, node);
|
|
24
|
+
const invokes = said(of('invokes'));
|
|
25
|
+
if (id !== undefined && invokes !== undefined) {
|
|
26
|
+
found.push({ id, invokes, carries: said(of('arrives')) !== undefined && listed(of('requires')).length > 0 });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
ts.forEachChild(node, visit);
|
|
30
|
+
};
|
|
31
|
+
visit(file);
|
|
32
|
+
return { asking: found, rows };
|
|
33
|
+
}
|
|
34
|
+
const fieldOf = (node, name) => {
|
|
35
|
+
for (const one of node.properties) {
|
|
36
|
+
if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name)
|
|
37
|
+
return one.initializer;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
export const slopnator = {
|
|
42
|
+
holds: (_node, at) => {
|
|
43
|
+
const { asking, rows } = invoking(at.text);
|
|
44
|
+
if (asking.length === 0)
|
|
45
|
+
return false;
|
|
46
|
+
const found = [];
|
|
47
|
+
const pass = descend();
|
|
48
|
+
for (const one of asking) {
|
|
49
|
+
const here = rows.get(one.invokes);
|
|
50
|
+
const named = here === undefined ? undefined : listed(fieldOf(here, 'webhooks'));
|
|
51
|
+
const signs = here === undefined ? undefined : yes(fieldOf(here, 'signed'));
|
|
52
|
+
const runs = pass.stated.get(one.invokes);
|
|
53
|
+
if (here === undefined && runs === undefined) {
|
|
54
|
+
found.push(one.id + ' invokes ' + one.invokes + ', and nothing in this tree declares an operation by that name');
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!(named ?? listed(runs?.fields.get('webhooks'))).includes(one.id)) {
|
|
58
|
+
found.push(one.id + ' invokes ' + one.invokes + ', and that operation names no webhook ' + one.id +
|
|
59
|
+
' back, so it is reachable from outside by something it never agreed to');
|
|
60
|
+
}
|
|
61
|
+
if ((signs ?? yes(runs?.fields.get('signed'))) && !one.carries) {
|
|
62
|
+
found.push(one.invokes + ' takes only a signed webhook, and ' + one.id +
|
|
63
|
+
' states no header a signature arrives on and no secret to check it against');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return found.length === 0 ? false : found.join('; ');
|
|
67
|
+
},
|
|
68
|
+
proves: {
|
|
69
|
+
wrong: [
|
|
70
|
+
{
|
|
71
|
+
files: [
|
|
72
|
+
{ 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" },
|
|
73
|
+
{ 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" },
|
|
74
|
+
{ 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" },
|
|
75
|
+
],
|
|
76
|
+
at: 'threads/_probe_webhook.ts',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
right: [
|
|
80
|
+
{
|
|
81
|
+
files: [
|
|
82
|
+
{ 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" },
|
|
83
|
+
{ 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" },
|
|
84
|
+
{ 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" },
|
|
85
|
+
],
|
|
86
|
+
at: 'threads/_probe_webhook.ts',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { LISTS as LINEAGE } from '../../core/descend.js';
|
|
3
|
+
const LISTS = new Set(LINEAGE);
|
|
4
|
+
const ROUTE = '/route.ts';
|
|
5
|
+
const said = (held) => held !== undefined && ts.isStringLiteral(held) ? held.text : undefined;
|
|
6
|
+
const fieldOf = (node, name) => {
|
|
7
|
+
for (const one of node.properties) {
|
|
8
|
+
if (ts.isPropertyAssignment(one) && ts.isIdentifier(one.name) && one.name.text === name)
|
|
9
|
+
return one.initializer;
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
export const slopnator = {
|
|
14
|
+
holds: (_node, at) => {
|
|
15
|
+
const file = ts.createSourceFile('row.ts', at.text, ts.ScriptTarget.Latest, true);
|
|
16
|
+
const routes = new Map();
|
|
17
|
+
const permitted = new Set();
|
|
18
|
+
const visit = (node) => {
|
|
19
|
+
if (ts.isPropertyAssignment(node) &&
|
|
20
|
+
ts.isIdentifier(node.name) &&
|
|
21
|
+
LISTS.has(node.name.text) &&
|
|
22
|
+
ts.isArrayLiteralExpression(node.initializer)) {
|
|
23
|
+
const list = node.name.text;
|
|
24
|
+
for (const one of node.initializer.elements) {
|
|
25
|
+
if (!ts.isObjectLiteralExpression(one))
|
|
26
|
+
continue;
|
|
27
|
+
const where = said(fieldOf(one, 'at'));
|
|
28
|
+
if (where === undefined || !where.endsWith(ROUTE))
|
|
29
|
+
continue;
|
|
30
|
+
routes.set(where, list);
|
|
31
|
+
if (list === 'derives' && said(fieldOf(one, 'by')) !== undefined && said(fieldOf(one, 'runs')) !== undefined) {
|
|
32
|
+
permitted.add(where);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
ts.forEachChild(node, visit);
|
|
37
|
+
};
|
|
38
|
+
visit(file);
|
|
39
|
+
const found = [];
|
|
40
|
+
for (const [where, list] of routes) {
|
|
41
|
+
if (permitted.has(where))
|
|
42
|
+
continue;
|
|
43
|
+
found.push(where + ' is named by a ' + list + ' row' +
|
|
44
|
+
(list === 'derives' ? ' that states no by and no runs' : '') +
|
|
45
|
+
', and a route is only ever derived');
|
|
46
|
+
}
|
|
47
|
+
return found.length === 0 ? false : found.join('; ');
|
|
48
|
+
},
|
|
49
|
+
proves: {
|
|
50
|
+
wrong: ["const M = { seeds: [{ at: 'app/inbound/wh_stripe/route.ts' }] }"],
|
|
51
|
+
right: [
|
|
52
|
+
{
|
|
53
|
+
files: [
|
|
54
|
+
{ at: 'threads/thread.ts', source: "export const THREAD = {\n declares: [\n { at: 'threads/_probe_route.ts' },\n ],\n}\n" },
|
|
55
|
+
{ 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" },
|
|
56
|
+
],
|
|
57
|
+
at: 'threads/_probe_route.ts',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { here, islandsDeclared } from
|
|
2
|
-
import { findTree } from
|
|
1
|
+
import { here, islandsDeclared } from './sl_unthreaded.js';
|
|
2
|
+
import { findTree } from '../../core/tree.js';
|
|
3
3
|
const TREE = `${findTree().name}/`;
|
|
4
4
|
const GOVERNS = /^\s*governs:\s*'([^']+)'/gm;
|
|
5
5
|
export const slopnator = {
|
|
@@ -47,7 +47,7 @@ export const slopnator = {
|
|
|
47
47
|
files: [
|
|
48
48
|
{
|
|
49
49
|
at: 'threads/_governed_probe.ts',
|
|
50
|
-
source: "export const M: Islanding = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['
|
|
50
|
+
source: "export const M: Islanding = {\n declares: [\n { id: 'events', at: 'packages/core/spec/src/islands/events.ts', governs: ['operations/'] },\n ],\n}\n",
|
|
51
51
|
},
|
|
52
52
|
],
|
|
53
53
|
at: 'threads/_governed_probe.ts',
|
|
@@ -50,7 +50,7 @@ export const slopnator: Slopnator = {
|
|
|
50
50
|
files: [
|
|
51
51
|
{
|
|
52
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: ['
|
|
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
54
|
},
|
|
55
55
|
],
|
|
56
56
|
at: 'threads/_governed_probe.ts',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ROOT } from '../../core/descend.js';
|
|
2
|
+
import { valuesIn } from '../../core/rows.js';
|
|
3
|
+
import { kind } from '../../integrations/disk.js';
|
|
4
|
+
import { REACH, programOf, unlisted } from '../../integrations/target.js';
|
|
5
|
+
const TARGETS = 'TARGETS';
|
|
6
|
+
const there = (path) => kind(path) === 'file';
|
|
7
|
+
export const slopnator = {
|
|
8
|
+
holds: (_node, at) => {
|
|
9
|
+
let held;
|
|
10
|
+
try {
|
|
11
|
+
held = valuesIn(at.text);
|
|
12
|
+
}
|
|
13
|
+
catch (thrown) {
|
|
14
|
+
if (!at.text.includes(TARGETS))
|
|
15
|
+
return false;
|
|
16
|
+
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));
|
|
17
|
+
}
|
|
18
|
+
const found = [];
|
|
19
|
+
for (const one of held.TARGETS ?? []) {
|
|
20
|
+
if (typeof one.id !== 'string' || !one.id.startsWith(REACH.cli))
|
|
21
|
+
continue;
|
|
22
|
+
const name = one.id.slice(REACH.cli.length);
|
|
23
|
+
if (programOf(name, ROOT, there) === undefined)
|
|
24
|
+
found.push(unlisted(name));
|
|
25
|
+
}
|
|
26
|
+
return found.length === 0 ? false : found.join('. ');
|
|
27
|
+
},
|
|
28
|
+
proves: {
|
|
29
|
+
wrong: [
|
|
30
|
+
"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: [] }]",
|
|
31
|
+
],
|
|
32
|
+
right: [
|
|
33
|
+
"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: [] }]",
|
|
34
|
+
"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: [] }]",
|
|
35
|
+
"export const TARGETS: readonly Target[] = [{ id: 'cli_node', of: 'in_node', describes: 'The runtime this framework runs on.', version: 1, takes: [], requires: [] }]",
|
|
36
|
+
"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' }]",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
"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: [] }]",
|
|
35
|
+
"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: [] }]",
|
|
36
|
+
"export const TARGETS: readonly Target[] = [{ id: 'cli_node', of: 'in_node', describes: 'The runtime this framework runs on.', version: 1, takes: [], requires: [] }]",
|
|
37
|
+
"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' }]",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
}
|