@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
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { execPath } from 'node:process'
|
|
3
|
+
|
|
1
4
|
import type { Member } from '../core/thread.ts'
|
|
5
|
+
import type { Actor } from '../core/actor.ts'
|
|
2
6
|
|
|
3
7
|
import { SECRECY, secrecyOf, type Secret } from './secret.ts'
|
|
4
8
|
|
|
@@ -7,21 +11,28 @@ export const REACH = {
|
|
|
7
11
|
pglite: 'pl_',
|
|
8
12
|
browser: 'zu_',
|
|
9
13
|
https: 'ht_',
|
|
10
|
-
module: 'mod_',
|
|
11
14
|
cli: 'cli_',
|
|
15
|
+
disk: 'dsk_',
|
|
16
|
+
sdk: 'sdk_',
|
|
12
17
|
} as const satisfies Readonly<Record<string, string>>
|
|
13
18
|
|
|
14
19
|
export type Reach = keyof typeof REACH
|
|
15
20
|
|
|
21
|
+
export const ADDRESS = {
|
|
22
|
+
row: 'reached at the address written on its row, which anybody may read',
|
|
23
|
+
secret: 'reached at a secret, because its address is a connection string and a connection string carries a password',
|
|
24
|
+
nothing: 'addressed by nothing',
|
|
25
|
+
} as const satisfies Readonly<Record<string, string>>
|
|
26
|
+
|
|
27
|
+
export type Addressed = keyof typeof ADDRESS
|
|
28
|
+
|
|
16
29
|
export interface Holds {
|
|
17
30
|
|
|
18
|
-
readonly takes: 'entities' | 'calls' |
|
|
31
|
+
readonly takes: 'entities' | 'calls' | null
|
|
19
32
|
|
|
20
33
|
readonly from: 'server' | 'browser' | 'either'
|
|
21
34
|
|
|
22
|
-
readonly addressed:
|
|
23
|
-
|
|
24
|
-
readonly arrives: 'npm' | 'machine' | null
|
|
35
|
+
readonly addressed: Addressed
|
|
25
36
|
|
|
26
37
|
readonly migrations: boolean
|
|
27
38
|
|
|
@@ -33,46 +44,279 @@ export interface Holds {
|
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
export const HOLDS: Readonly<Record<Reach, Holds>> = {
|
|
36
|
-
postgres: { takes: 'entities', from: 'server', addressed:
|
|
37
|
-
pglite: { takes: 'entities', from: 'either', addressed:
|
|
38
|
-
browser: { takes: 'entities', from: 'browser', addressed:
|
|
39
|
-
https: { takes: 'calls', from: 'either', addressed:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
postgres: { takes: 'entities', from: 'server', addressed: 'secret', migrations: true, references: true, sql: true, driver: 'postgres' },
|
|
48
|
+
pglite: { takes: 'entities', from: 'either', addressed: 'nothing', migrations: true, references: true, sql: true, driver: '@electric-sql/pglite' },
|
|
49
|
+
browser: { takes: 'entities', from: 'browser', addressed: 'nothing', migrations: false, references: false, sql: false, driver: 'zustand' },
|
|
50
|
+
https: { takes: 'calls', from: 'either', addressed: 'row', migrations: false, references: false, sql: false, driver: null },
|
|
51
|
+
cli: { takes: 'calls', from: 'server', addressed: 'nothing', migrations: false, references: false, sql: false, driver: null },
|
|
52
|
+
disk: { takes: null, from: 'server', addressed: 'nothing', migrations: false, references: false, sql: false, driver: null },
|
|
53
|
+
sdk: { takes: 'calls', from: 'server', addressed: 'nothing', migrations: false, references: false, sql: false, driver: null },
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
export const SAID: Readonly<Record<Reach, string>> = {
|
|
46
57
|
postgres:
|
|
47
58
|
'A postgres this app connects to over the network. It keeps entities and holds everything a declaration can state about their shape — tables, references and invariants — so what is kept here is held to that shape by the database itself rather than by whatever remembered to check. Who may touch a row is not asked of it: that is answered by the app, against the caller who actually arrived. It is reached from a server only: a connection to it from a page is a database somebody else can open. This is where a thing goes when losing it would matter.',
|
|
48
59
|
pglite:
|
|
49
|
-
'A postgres running inside the process that opened it. It keeps entities and holds tables, references and invariants, exactly as the networked one does — what differs is who can reach it, not what it can be told to hold. What belongs here is what the caller is already entitled to see: a local copy, a working set, an offline cache.',
|
|
60
|
+
'A postgres running inside the process that opened it. It keeps entities and holds tables, references and invariants, exactly as the networked one does — what differs is who can reach it, not what it can be told to hold. It is addressed by nothing: its files sit under .thread beside the tree, in a folder named after the target, so nobody types a path and a fresh clone starts it empty. What belongs here is what the caller is already entitled to see: a local copy, a working set, an offline cache.',
|
|
50
61
|
browser:
|
|
51
62
|
'A store in the browser, held in memory the viewer owns. It keeps entities and it is addressed by nothing, because it is already where the code runs. It holds no migration, because a page load builds it from nothing every time. What belongs here is what the interface needs to be responsive and can afford to lose.',
|
|
52
63
|
https:
|
|
53
|
-
'Something this app calls over https — a vendor, a service, another app. It accepts calls rather than entities: each is declared with the verb and the path it takes, so a node names the call and never writes a url.
|
|
54
|
-
module:
|
|
55
|
-
'Something this app brings in from outside the repository and calls — an npm package, an SDK, an installer. Declaring one installs it and reads the methods it publishes, then asks which of them this app may call. What is picked is written down, and a method that is not on that list is refused before the package is imported: what can ever run is fixed when the target is declared, not by whoever calls it. A credential is named only if it needs one, and it comes from the vault at the moment of the call and is never written anywhere. A package you only import is a dependency rather than one of these, and the manifest already declares it.',
|
|
64
|
+
'Something this app calls over https — a vendor, a service, another app. It accepts calls rather than entities: each is declared with the verb and the path it takes, so a node names the call and never writes a url. Where it is goes on its row as address, and a header every call carries goes on its row as headers, because both are facts anybody may read; a credential it sends is a secret, and requiring one is what keeps the target out of a browser.',
|
|
56
65
|
cli:
|
|
57
|
-
'A program this app runs as a command
|
|
66
|
+
'A program this app runs as a command. git is the one that made this necessary. It is not installed, because it is not a dependency: it is on the machine or it is not, and a run finds out. What it accepts is the words it is asked with, declared beside it as calls with thread create target call: a subcommand where the program has them, and the option that selects what it does where it has none, each spelled the way the program spells it. A word that is not declared never runs. The program is the one the target is named after, so declaring one runs nothing and reads nothing off the machine. An operation that requires it is handed one function per declared word, and that is the only way this app ever runs it.',
|
|
67
|
+
disk:
|
|
68
|
+
'The disk this app runs on. It answers a fixed set of orders — is this there, read it, write it, list what is beside it, how big it is, remove it, and what it really is — and those orders are the same for every disk, because they are what a filesystem does rather than what any one of them chose to offer. None of them runs anything: a disk holds files and a program that installs packages is not a file order, which is why installing what a tree depends on happens where somebody typed the command and is not something a disk can be asked for. It is reached from a server only: a page has no filesystem. A trial stands one of these up out of its own scenario, so a rule can be measured against files that exist for the length of that run and nothing is written where you work.',
|
|
69
|
+
sdk:
|
|
70
|
+
'A package this app calls inside its own process — a vendor\'s SDK, fetched from npm, a git link or a tarball. It accepts calls rather than entities: the file this target declares exports `calls`, each call names the function there it runs as its path, and that file is the only place the package is imported, so an operation names the call and never the package. The integration it is part of names what to fetch in needs, and declaring the integration fetches it. It runs where the app runs, so it is addressed by nothing and reached from a server only.',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const on = (host: string, names: readonly string[]): readonly string[] => names.map((one) => host + '.' + one)
|
|
74
|
+
|
|
75
|
+
const driverOfReach = (one: Reach): readonly string[] => {
|
|
76
|
+
const driver = HOLDS[one].driver
|
|
77
|
+
return driver === null ? [] : ['import ' + driver]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const OPENS: Readonly<Record<Reach, readonly string[]>> = {
|
|
81
|
+
postgres: driverOfReach('postgres'),
|
|
82
|
+
pglite: driverOfReach('pglite'),
|
|
83
|
+
browser: driverOfReach('browser'),
|
|
84
|
+
https: [
|
|
85
|
+
'fetch',
|
|
86
|
+
'XMLHttpRequest',
|
|
87
|
+
'WebSocket',
|
|
88
|
+
'EventSource',
|
|
89
|
+
...on('window', ['fetch']),
|
|
90
|
+
...on('navigator', ['sendBeacon']),
|
|
91
|
+
'import node:http',
|
|
92
|
+
'import node:https',
|
|
93
|
+
'import node:http2',
|
|
94
|
+
'import node:net',
|
|
95
|
+
'import node:tls',
|
|
96
|
+
'import node:dgram',
|
|
97
|
+
'import node:dns',
|
|
98
|
+
],
|
|
99
|
+
cli: ['import node:child_process', 'import node:worker_threads', 'import node:cluster'],
|
|
100
|
+
disk: ['import node:fs', 'import node:fs/promises'],
|
|
101
|
+
sdk: [],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type Kind = 'file' | 'folder' | 'other' | 'none'
|
|
105
|
+
|
|
106
|
+
export interface Listed {
|
|
107
|
+
readonly name: string
|
|
108
|
+
|
|
109
|
+
readonly kind: Exclude<Kind, 'none'>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface Measured {
|
|
113
|
+
readonly size: number
|
|
114
|
+
|
|
115
|
+
readonly modified: number
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface Answering {
|
|
119
|
+
readonly kind: (at: string) => Kind
|
|
120
|
+
readonly read: (at: string) => string
|
|
121
|
+
readonly write: (at: string, said: string, mode?: number) => void
|
|
122
|
+
readonly list: (at: string) => readonly Listed[]
|
|
123
|
+
readonly stat: (at: string) => Measured
|
|
124
|
+
readonly remove: (at: string) => void
|
|
125
|
+
readonly real: (at: string) => string
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface Attached extends Answering {
|
|
129
|
+
|
|
130
|
+
readonly fetch: (at: string) => string
|
|
131
|
+
|
|
132
|
+
readonly run: (at: string, words: readonly string[]) => Ran
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const ORDERS = {
|
|
136
|
+
kind: 'kind',
|
|
137
|
+
read: 'read',
|
|
138
|
+
write: 'written',
|
|
139
|
+
list: 'list',
|
|
140
|
+
stat: 'stat',
|
|
141
|
+
remove: 'removed',
|
|
142
|
+
real: 'real',
|
|
143
|
+
fetch: 'fetched',
|
|
144
|
+
run: 'ran',
|
|
145
|
+
} as const
|
|
146
|
+
|
|
147
|
+
export interface Order {
|
|
148
|
+
readonly order: keyof typeof ORDERS
|
|
149
|
+
readonly at: string
|
|
150
|
+
readonly said?: string
|
|
151
|
+
readonly mode?: number
|
|
152
|
+
readonly words?: readonly string[]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface Answer {
|
|
156
|
+
readonly answered: string
|
|
157
|
+
readonly found?: unknown
|
|
158
|
+
readonly said?: string
|
|
159
|
+
readonly at?: string
|
|
160
|
+
readonly because?: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface Recorded {
|
|
164
|
+
readonly order: Order
|
|
165
|
+
readonly answered: Answer
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface How {
|
|
169
|
+
readonly at?: string
|
|
170
|
+
readonly hears?: string
|
|
171
|
+
readonly of?: string
|
|
172
|
+
readonly answered?: readonly Answer[]
|
|
173
|
+
readonly recording?: readonly Recorded[]
|
|
174
|
+
readonly answering?: number
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface Ran {
|
|
178
|
+
|
|
179
|
+
readonly there: boolean
|
|
180
|
+
|
|
181
|
+
readonly ok: boolean
|
|
182
|
+
|
|
183
|
+
readonly code: number
|
|
184
|
+
|
|
185
|
+
readonly out: string
|
|
186
|
+
|
|
187
|
+
readonly err: string
|
|
188
|
+
|
|
189
|
+
readonly killed: string | null
|
|
190
|
+
|
|
191
|
+
readonly why: string
|
|
192
|
+
|
|
193
|
+
readonly of?: string
|
|
194
|
+
|
|
195
|
+
readonly asks?: readonly Order[]
|
|
196
|
+
|
|
197
|
+
readonly lost?: string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export type Running<C extends string = string> = Readonly<
|
|
201
|
+
Record<
|
|
202
|
+
C,
|
|
203
|
+
(
|
|
204
|
+
args: readonly string[],
|
|
205
|
+
shows?: boolean | 'errors' | 'tee',
|
|
206
|
+
within?: number,
|
|
207
|
+
how?: How,
|
|
208
|
+
) => Promise<Ran>
|
|
209
|
+
>
|
|
210
|
+
>
|
|
211
|
+
|
|
212
|
+
export type Calling<C extends string = string> = { readonly [K in C]: (given?: unknown) => Promise<unknown> }
|
|
213
|
+
|
|
214
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
215
|
+
|
|
216
|
+
export interface HttpRequest {
|
|
217
|
+
readonly target: string
|
|
218
|
+
|
|
219
|
+
readonly address: string
|
|
220
|
+
readonly method: HttpMethod
|
|
221
|
+
|
|
222
|
+
readonly path: string
|
|
223
|
+
|
|
224
|
+
readonly headers?: Readonly<Record<string, string>>
|
|
225
|
+
|
|
226
|
+
readonly body?: Uint8Array
|
|
227
|
+
readonly contentType?: string
|
|
228
|
+
|
|
229
|
+
readonly idempotencyKey?: string
|
|
230
|
+
|
|
231
|
+
readonly told?: (said: unknown) => void
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface HttpResponse {
|
|
235
|
+
readonly status: number
|
|
236
|
+
readonly headers: Readonly<Record<string, string>>
|
|
237
|
+
readonly body: Uint8Array
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface Http {
|
|
241
|
+
call(request: HttpRequest, within?: number): Promise<HttpResponse>
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export type Row = Record<string, unknown>
|
|
245
|
+
|
|
246
|
+
export type PropertyValue = string | number | boolean | null | readonly (string | number | boolean)[]
|
|
247
|
+
|
|
248
|
+
export function isRow(v: unknown): v is Row {
|
|
249
|
+
return typeof v === 'object' && v !== null && typeof (v as { id?: unknown }).id === 'string'
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export type ReadField = `fld_${string}` | `rel_${string}` | (string & {})
|
|
253
|
+
|
|
254
|
+
export type FieldList = readonly [ReadField, ...ReadField[]]
|
|
255
|
+
|
|
256
|
+
export interface Store {
|
|
257
|
+
insert(actor: Actor, entity: string, row: Row): Promise<Row>
|
|
258
|
+
|
|
259
|
+
get(actor: Actor, entity: string, key: string | number, fields: FieldList): Promise<Row | null>
|
|
260
|
+
|
|
261
|
+
list(
|
|
262
|
+
actor: Actor,
|
|
263
|
+
entity: string,
|
|
264
|
+
opts?: { limit?: number; where?: Readonly<Record<string, string | number | boolean | null>> },
|
|
265
|
+
): Promise<Row[]>
|
|
266
|
+
|
|
267
|
+
update(
|
|
268
|
+
actor: Actor,
|
|
269
|
+
entity: string,
|
|
270
|
+
key: string | number,
|
|
271
|
+
patch: Record<string, unknown>,
|
|
272
|
+
expectedVersion?: number,
|
|
273
|
+
): Promise<Row>
|
|
274
|
+
|
|
275
|
+
remove(actor: Actor, entity: string, key: string | number): Promise<void>
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface Transactional {
|
|
279
|
+
transaction<T>(actor: Actor, fn: () => Promise<T>): Promise<T>
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function isTransactional(store: Store): store is Store & Transactional {
|
|
283
|
+
return typeof (store as Partial<Transactional>).transaction === 'function'
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface Integration extends Member {
|
|
287
|
+
|
|
288
|
+
readonly needs?: Readonly<Record<string, string>>
|
|
58
289
|
}
|
|
59
290
|
|
|
60
291
|
export interface Target extends Member {
|
|
61
292
|
|
|
293
|
+
readonly of: string
|
|
294
|
+
|
|
62
295
|
readonly takes: readonly string[]
|
|
63
296
|
|
|
64
297
|
readonly requires: readonly string[]
|
|
65
298
|
|
|
66
|
-
readonly
|
|
299
|
+
readonly address?: string
|
|
67
300
|
|
|
68
|
-
readonly
|
|
301
|
+
readonly headers?: Readonly<Record<string, string>>
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export interface Webhook extends Member {
|
|
305
|
+
|
|
306
|
+
readonly of: string
|
|
307
|
+
|
|
308
|
+
readonly path: string
|
|
309
|
+
|
|
310
|
+
readonly arrives?: string
|
|
311
|
+
|
|
312
|
+
readonly invokes: string
|
|
69
313
|
}
|
|
70
314
|
|
|
71
315
|
export interface Call extends Member {
|
|
72
316
|
|
|
73
317
|
readonly of: string
|
|
74
318
|
|
|
75
|
-
readonly method
|
|
319
|
+
readonly method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
76
320
|
|
|
77
321
|
readonly path: string
|
|
78
322
|
}
|
|
@@ -87,27 +331,35 @@ export function reachOf(id: string): Reach | undefined {
|
|
|
87
331
|
return (Object.keys(REACH) as Reach[]).find((one) => id.startsWith(REACH[one]))
|
|
88
332
|
}
|
|
89
333
|
|
|
334
|
+
export const standsHere = (one: Target, targets: readonly Target[]): boolean =>
|
|
335
|
+
!one.requires.some((each) => targets.some((two) => two.id === each))
|
|
336
|
+
|
|
90
337
|
export function holdsOf(id: string): Holds | undefined {
|
|
91
338
|
const what = reachOf(id)
|
|
92
339
|
return what === undefined ? undefined : HOLDS[what]
|
|
93
340
|
}
|
|
94
341
|
|
|
342
|
+
export function requirable(targets: readonly Target[], calls: readonly Call[]): readonly Target[] {
|
|
343
|
+
const declared = new Set(calls.map((one) => one.id))
|
|
344
|
+
return targets.filter((one) => holdsOf(one.id)?.takes !== 'calls' || one.takes.some((each) => declared.has(each)))
|
|
345
|
+
}
|
|
346
|
+
|
|
95
347
|
export function driverOf(id: string): string | null {
|
|
96
348
|
return holdsOf(id)?.driver ?? null
|
|
97
349
|
}
|
|
98
350
|
|
|
99
351
|
export function addressOf(one: Target, secrets: readonly Secret[]): Secret | undefined {
|
|
100
352
|
|
|
101
|
-
if (holdsOf(one.id)?.addressed !==
|
|
353
|
+
if (holdsOf(one.id)?.addressed !== 'secret') return undefined
|
|
102
354
|
const held = new Map(secrets.map((each) => [each.id, each]))
|
|
103
|
-
return one.requires.map((each) => held.get(each)).find((each) => each !== undefined && each.sends === undefined)
|
|
355
|
+
return one.requires.map((each) => held.get(each)).find((each) => each !== undefined && each.sends === undefined && each.signs === undefined)
|
|
104
356
|
}
|
|
105
357
|
|
|
106
358
|
export function credentialsOf(one: Target, secrets: readonly Secret[]): readonly Secret[] {
|
|
107
359
|
const held = new Map(secrets.map((each) => [each.id, each]))
|
|
108
360
|
return one.requires
|
|
109
361
|
.map((each) => held.get(each))
|
|
110
|
-
.filter((each): each is Secret => each !== undefined && each.sends !== undefined)
|
|
362
|
+
.filter((each): each is Secret => each !== undefined && (each.sends !== undefined || each.signs !== undefined))
|
|
111
363
|
}
|
|
112
364
|
|
|
113
365
|
export function servesBrowser(one: Target, secrets: readonly Secret[]): true | string {
|
|
@@ -144,28 +396,50 @@ export const unreachable = (targets: readonly Target[]): readonly string[] =>
|
|
|
144
396
|
'nothing can tell how to reach it — and how a thing is reached decides what may be declared for it',
|
|
145
397
|
)
|
|
146
398
|
|
|
147
|
-
const
|
|
399
|
+
export const LOCKFILES: Readonly<Record<string, string>> = {
|
|
400
|
+
'pnpm-lock.yaml': 'pnpm',
|
|
401
|
+
'yarn.lock': 'yarn',
|
|
402
|
+
'bun.lockb': 'bun',
|
|
403
|
+
'package-lock.json': 'npm',
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export const PACKAGER = 'packager'
|
|
407
|
+
|
|
408
|
+
const NAMED: readonly string[] = ['git', 'thread']
|
|
409
|
+
|
|
410
|
+
const RUNTIME = 'node'
|
|
148
411
|
|
|
149
|
-
|
|
412
|
+
export const managerOf = (at: string, there: (path: string) => boolean): string =>
|
|
413
|
+
Object.entries(LOCKFILES).find(([lock]) => there(join(at, lock)))?.[1] ?? 'npm'
|
|
150
414
|
|
|
151
|
-
|
|
415
|
+
export function programOf(name: string, at: string, there: (path: string) => boolean): string | undefined {
|
|
416
|
+
if (NAMED.includes(name)) return name
|
|
417
|
+
if (name === RUNTIME) return execPath
|
|
418
|
+
if (name === PACKAGER) return managerOf(at, there)
|
|
419
|
+
const installed = join(at, 'node_modules', '.bin', name)
|
|
420
|
+
return there(installed) ? installed : undefined
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export const unlisted = (name: string): string =>
|
|
424
|
+
'`' + REACH.cli + name + '` names ' + name + ', and nothing lists a program by that name: it is not under node_modules/.bin, so this tree did not install it; it is not git, node or thread; and it is not `' + PACKAGER + '`, the word for whichever package manager the lockfile picks. A program the tree did not install is whatever the machine happens to have, and a target on that is a shell by another name. Add the package that provides it and declare the target again, or ask for it through a declared call on `' + REACH.cli + PACKAGER + '`'
|
|
425
|
+
|
|
426
|
+
const SHAPED: Readonly<Record<'cli', RegExp>> = {
|
|
427
|
+
cli: /^-{0,2}[A-Za-z][A-Za-z0-9-]*$/,
|
|
152
428
|
}
|
|
153
429
|
|
|
154
430
|
const EVERY_VALUE = new Set(['constructor', '__proto__', 'prototype'])
|
|
155
431
|
|
|
156
432
|
export function misnamed(target: string, name: string): string | undefined {
|
|
157
433
|
const reach = reachOf(target)
|
|
158
|
-
if (reach !== '
|
|
434
|
+
if (reach !== 'cli') return undefined
|
|
159
435
|
if (EVERY_VALUE.has(name)) {
|
|
160
436
|
return (
|
|
161
|
-
`\`${name}\` is a step every JavaScript value has rather than something \`${target}\`
|
|
437
|
+
`\`${name}\` is a step every JavaScript value has rather than something \`${target}\` answers to, and walking to ` +
|
|
162
438
|
'it leaves what was declared'
|
|
163
439
|
)
|
|
164
440
|
}
|
|
165
441
|
if (!SHAPED[reach].test(name)) {
|
|
166
|
-
return
|
|
167
|
-
? `\`${name}\` is not a method path — one name, or names joined by dots, e.g. \`customers.list\``
|
|
168
|
-
: `\`${name}\` is not a subcommand — one word as the program spells it, e.g. \`commit\` or \`rev-parse\``
|
|
442
|
+
return name + ' is not one word as the program spells it. Give a subcommand, e.g. commit or rev-parse, or, where the program has none, the option that selects what it does, e.g. -p'
|
|
169
443
|
}
|
|
170
444
|
return undefined
|
|
171
445
|
}
|
|
@@ -185,12 +459,8 @@ export function misaccepted(
|
|
|
185
459
|
if (holds.takes === 'entities' && isEntity.has(named)) continue
|
|
186
460
|
if (holds.takes === 'calls' && isCall.has(named)) continue
|
|
187
461
|
|
|
188
|
-
if (holds.takes ===
|
|
189
|
-
found.push(
|
|
190
|
-
`\`${one.id}\` is reached as ${String(reachOf(one.id))} and its \`takes\` names "${named}" — what an ` +
|
|
191
|
-
'installed package or a program responds to goes in `offers`, not here. `takes` is a demand, and the ' +
|
|
192
|
-
'descent requires every word on one to be coined by a row in this tree; a method name never can be',
|
|
193
|
-
)
|
|
462
|
+
if (holds.takes === null) {
|
|
463
|
+
found.push(`\`${one.id}\` takes "${named}", and a ${String(reachOf(one.id))} takes nothing: it answers the orders every ${String(reachOf(one.id))} answers, stated once on the target itself`)
|
|
194
464
|
continue
|
|
195
465
|
}
|
|
196
466
|
found.push(
|
|
@@ -202,21 +472,52 @@ export function misaccepted(
|
|
|
202
472
|
return found
|
|
203
473
|
}
|
|
204
474
|
|
|
475
|
+
export const placedAt = (targets: readonly Target[], id: string): string => targets.find((one) => one.id === id)?.address ?? ''
|
|
476
|
+
|
|
477
|
+
export function placed(one: Target): readonly string[] {
|
|
478
|
+
const said = one.address
|
|
479
|
+
if (said === undefined) {
|
|
480
|
+
return [one.id + ' is reached over https and its row says nothing about where. Give it an address: the absolute url every call on it is written from, scheme and host and nothing after the path']
|
|
481
|
+
}
|
|
482
|
+
let held: URL
|
|
483
|
+
try {
|
|
484
|
+
held = new URL(said)
|
|
485
|
+
} catch {
|
|
486
|
+
return [one.id + ' has the address ' + JSON.stringify(said) + ', and that is not an absolute url. Write the whole of it, scheme and host']
|
|
487
|
+
}
|
|
488
|
+
if ([held.search, held.hash].some((each) => each !== '')) {
|
|
489
|
+
return [one.id + ' has the address ' + said + ', which carries a query or a fragment. Those belong to a call, so an address ends at its path']
|
|
490
|
+
}
|
|
491
|
+
const wrong = Object.entries(one.headers ?? {}).filter(([, value]) => typeof value !== 'string')
|
|
492
|
+
if (wrong.length > 0) {
|
|
493
|
+
return [one.id + ' carries the header ' + String(wrong[0]?.[0] ?? '') + ' with a value that is not text. A header every call carries is written as text, exactly as it goes on the wire']
|
|
494
|
+
}
|
|
495
|
+
return []
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const stated = (one: Target, holds: Holds): readonly string[] => {
|
|
499
|
+
if (holds.addressed === 'row') return placed(one)
|
|
500
|
+
const carried = [...(one.address === undefined ? [] : ['an address']), ...(one.headers === undefined ? [] : ['headers'])]
|
|
501
|
+
if (carried.length === 0) return []
|
|
502
|
+
return [one.id + ' carries ' + carried.join(' and ') + ', and a ' + String(reachOf(one.id)) + ' is ' + ADDRESS[holds.addressed] + '. Take it off the row']
|
|
503
|
+
}
|
|
504
|
+
|
|
205
505
|
export function unaddressed(targets: readonly Target[], secrets: readonly Secret[]): readonly string[] {
|
|
206
506
|
const held = new Map(secrets.map((one) => [one.id, one]))
|
|
207
507
|
const found: string[] = []
|
|
208
508
|
for (const one of targets) {
|
|
209
509
|
const holds = holdsOf(one.id)
|
|
210
510
|
if (holds === undefined) continue
|
|
511
|
+
found.push(...stated(one, holds))
|
|
211
512
|
const addresses = one.requires
|
|
212
513
|
.map((each) => held.get(each))
|
|
213
|
-
|
|
214
|
-
if (
|
|
514
|
+
.filter((each): each is Secret => each !== undefined && each.sends === undefined && each.signs === undefined)
|
|
515
|
+
if (holds.addressed !== 'secret') {
|
|
215
516
|
|
|
216
|
-
if (
|
|
517
|
+
if (addresses.length > 0) {
|
|
217
518
|
found.push(
|
|
218
|
-
|
|
219
|
-
|
|
519
|
+
one.id + ' is reached over ' + String(reachOf(one.id)) + ', which is ' + ADDRESS[holds.addressed] + ', and it requires ' +
|
|
520
|
+
(addresses[0]?.id ?? '') + ', a secret with nowhere to be sent. A secret is a credential: give it the header it goes on with sends, or take it off this row',
|
|
220
521
|
)
|
|
221
522
|
}
|
|
222
523
|
continue
|
|
@@ -239,14 +540,15 @@ export function unaddressed(targets: readonly Target[], secrets: readonly Secret
|
|
|
239
540
|
}
|
|
240
541
|
|
|
241
542
|
export function unheld(targets: readonly Target[], secrets: readonly Secret[]): readonly string[] {
|
|
242
|
-
const known = new Set(secrets.map((one) => one.id))
|
|
543
|
+
const known = new Set([...secrets.map((one) => one.id), ...targets.map((one) => one.id)])
|
|
243
544
|
const found: string[] = []
|
|
244
545
|
for (const one of targets) {
|
|
245
546
|
for (const named of one.requires) {
|
|
246
547
|
if (known.has(named)) continue
|
|
247
548
|
found.push(
|
|
248
|
-
`\`${one.id}\` requires "${named}", and this app declares no secret by that name. A secret is declared before it ` +
|
|
249
|
-
'is required, so the one place saying who
|
|
549
|
+
`\`${one.id}\` requires "${named}", and this app declares no secret and no target by that name. A secret is declared before it ` +
|
|
550
|
+
'is required, and a target before it is reached through, so the one place saying who holds a value, or ' +
|
|
551
|
+
'answers for a reach, is never whichever place happened to need it',
|
|
250
552
|
)
|
|
251
553
|
}
|
|
252
554
|
}
|
|
@@ -265,11 +567,25 @@ export function unanswered(calls: readonly Call[], targets: readonly Target[]):
|
|
|
265
567
|
const holds = holdsOf(of.id)
|
|
266
568
|
if (holds !== undefined && holds.takes !== 'calls') {
|
|
267
569
|
found.push(
|
|
268
|
-
|
|
269
|
-
|
|
570
|
+
holds.takes === 'entities'
|
|
571
|
+
? `\`${one.id}\` is a call on \`${of.id}\`, which is reached over ${String(reachOf(of.id))} and keeps entities. A place you keep rows in is not a place you issue verbs at`
|
|
572
|
+
: `\`${one.id}\` is a call on \`${of.id}\`, which is a ${String(reachOf(of.id))} and answers the orders every ${String(reachOf(of.id))} answers, so nothing is declared on it. A node is handed those orders by requiring it`,
|
|
270
573
|
)
|
|
271
574
|
continue
|
|
272
575
|
}
|
|
576
|
+
if (reachOf(of.id) === 'https' && one.method === undefined) {
|
|
577
|
+
found.push(`\`${one.id}\` is a call over https and states no method. A call over https says its verb: GET, POST, PUT, PATCH or DELETE`)
|
|
578
|
+
continue
|
|
579
|
+
}
|
|
580
|
+
if (reachOf(of.id) !== 'https' && one.method !== undefined) {
|
|
581
|
+
found.push(`\`${one.id}\` states the method ${one.method}, and \`${of.id}\` is a ${String(reachOf(of.id))}. A subcommand or a method has no http verb, so take \`method\` off that row`)
|
|
582
|
+
continue
|
|
583
|
+
}
|
|
584
|
+
const shaped = misnamed(of.id, one.path)
|
|
585
|
+
if (shaped !== undefined) {
|
|
586
|
+
found.push(`\`${one.id}\` is a call on \`${of.id}\`, and its path ${shaped}`)
|
|
587
|
+
continue
|
|
588
|
+
}
|
|
273
589
|
if (!of.takes.includes(one.id)) {
|
|
274
590
|
found.push(
|
|
275
591
|
`\`${one.id}\` is a call on \`${of.id}\` and \`${of.id}\` does not accept it. Add it to that target's \`takes\` — ` +
|
|
@@ -280,25 +596,24 @@ export function unanswered(calls: readonly Call[], targets: readonly Target[]):
|
|
|
280
596
|
return found
|
|
281
597
|
}
|
|
282
598
|
|
|
283
|
-
export function
|
|
599
|
+
export function unplaced(targets: readonly Target[], integrations: readonly Integration[]): readonly string[] {
|
|
600
|
+
const known = new Set(integrations.map((one) => one.id))
|
|
284
601
|
const found: string[] = []
|
|
285
602
|
for (const one of targets) {
|
|
286
|
-
const
|
|
287
|
-
if (
|
|
288
|
-
if (holds.arrives !== null && (one.uses ?? '') === '') {
|
|
603
|
+
const named = one.of ?? ''
|
|
604
|
+
if (named === '') {
|
|
289
605
|
found.push(
|
|
290
|
-
`\`${one.id}\`
|
|
291
|
-
|
|
292
|
-
'
|
|
606
|
+
`\`${one.id}\` names no integration, and every target is one way into something outside this app — a vendor, ` +
|
|
607
|
+
'a service, a program on the machine. Say which one it reaches into: `thread create integration` lists ' +
|
|
608
|
+
'the ones already declared and declares another',
|
|
293
609
|
)
|
|
294
610
|
continue
|
|
295
611
|
}
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
612
|
+
if (known.has(named)) continue
|
|
613
|
+
found.push(
|
|
614
|
+
`\`${one.id}\` is part of "${named}", and this app declares no integration by that name. Declare it with ` +
|
|
615
|
+
'`thread create integration`, or point this row at one already declared',
|
|
616
|
+
)
|
|
302
617
|
}
|
|
303
618
|
return found
|
|
304
619
|
}
|
|
@@ -308,14 +623,15 @@ export function refusals(
|
|
|
308
623
|
secrets: readonly Secret[],
|
|
309
624
|
calls: readonly Call[],
|
|
310
625
|
entities: readonly string[],
|
|
626
|
+
integrations: readonly Integration[],
|
|
311
627
|
): readonly string[] {
|
|
312
628
|
return [
|
|
313
629
|
...unreachable(targets),
|
|
630
|
+
...unplaced(targets, integrations),
|
|
314
631
|
...unheld(targets, secrets),
|
|
315
632
|
...unaddressed(targets, secrets),
|
|
316
633
|
...unanswered(calls, targets),
|
|
317
634
|
...misaccepted(targets, entities, calls),
|
|
318
|
-
...misused(targets),
|
|
319
635
|
]
|
|
320
636
|
}
|
|
321
637
|
|
|
@@ -11,4 +11,5 @@ export declare function keyed(root: string): Buffer;
|
|
|
11
11
|
export declare function seal(root: string, named: string, env: Env, value: string): void;
|
|
12
12
|
export declare const sealedIn: (root: string, env: Env) => readonly string[];
|
|
13
13
|
export declare function opened(root: string, declared: readonly string[], env: Env): (refs: readonly string[]) => Promise<Readonly<Record<string, string>>>;
|
|
14
|
+
export declare function unseal(root: string, named: string, envs: readonly Env[]): readonly Env[];
|
|
14
15
|
export declare const IGNORES: readonly string[];
|