@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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { Actor } from '../core/actor.ts'
|
|
2
|
+
import type { FieldList, Row, Store } from './target.ts'
|
|
3
|
+
|
|
4
|
+
const SHAPE = `
|
|
5
|
+
select table_name, column_name, data_type, is_nullable
|
|
6
|
+
from information_schema.columns
|
|
7
|
+
where table_schema = 'public'
|
|
8
|
+
order by table_name, column_name
|
|
9
|
+
`
|
|
10
|
+
|
|
11
|
+
const CONSTRAINTS = `
|
|
12
|
+
select tc.table_name, tc.constraint_type, kcu.column_name
|
|
13
|
+
from information_schema.table_constraints tc
|
|
14
|
+
left join information_schema.key_column_usage kcu on kcu.constraint_name = tc.constraint_name
|
|
15
|
+
where tc.table_schema = 'public'
|
|
16
|
+
order by tc.table_name, tc.constraint_type, kcu.column_name
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
interface Queried {
|
|
20
|
+
exec(sql: string): Promise<unknown>
|
|
21
|
+
query(sql: string, params?: readonly unknown[]): Promise<{ rows: readonly unknown[] }>
|
|
22
|
+
close(): Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MISSING =
|
|
26
|
+
'a postgres living inside this process needs `@electric-sql/pglite`, and it is not installed. No package here ' +
|
|
27
|
+
'declares it, because nothing needs one until a target is declared that keeps rows in one — install it in the ' +
|
|
28
|
+
'app that declares that target. Carrying on without having opened one would be the drift this exists to catch, ' +
|
|
29
|
+
'wearing a green tick'
|
|
30
|
+
|
|
31
|
+
async function opened(at?: string): Promise<Queried> {
|
|
32
|
+
let made: { PGlite: new (at?: string) => Queried }
|
|
33
|
+
try {
|
|
34
|
+
made = (await import('@electric-sql/pglite')) as unknown as { PGlite: new (at?: string) => Queried }
|
|
35
|
+
} catch {
|
|
36
|
+
throw new Error(MISSING)
|
|
37
|
+
}
|
|
38
|
+
return new made.PGlite(at)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const APPLIED = '"_thread_applied"'
|
|
42
|
+
|
|
43
|
+
async function caughtUp(made: Queried, history: readonly { readonly name: string; readonly sql: string }[]): Promise<void> {
|
|
44
|
+
if (history.length === 0) return
|
|
45
|
+
await made.exec('create table if not exists ' + APPLIED + ' ("name" text primary key)')
|
|
46
|
+
const done = new Set(
|
|
47
|
+
(await made.query('select "name" from ' + APPLIED)).rows.map((one) => String((one as { readonly name?: unknown }).name)),
|
|
48
|
+
)
|
|
49
|
+
for (const one of history) {
|
|
50
|
+
if (done.has(one.name)) continue
|
|
51
|
+
await made.exec(one.sql)
|
|
52
|
+
await made.query('insert into ' + APPLIED + ' ("name") values ($1)', [one.name])
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function shapeOf(sql: readonly string[]): Promise<string> {
|
|
57
|
+
const db = await opened()
|
|
58
|
+
try {
|
|
59
|
+
for (const one of sql) await db.exec(one)
|
|
60
|
+
const columns = await db.query(SHAPE)
|
|
61
|
+
const constraints = await db.query(CONSTRAINTS)
|
|
62
|
+
return JSON.stringify({ columns: columns.rows, constraints: constraints.rows }, null, 2)
|
|
63
|
+
} finally {
|
|
64
|
+
await db.close()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface Kept {
|
|
69
|
+
|
|
70
|
+
readonly name: string
|
|
71
|
+
|
|
72
|
+
readonly schema: string
|
|
73
|
+
|
|
74
|
+
readonly tables: Readonly<Record<string, string>>
|
|
75
|
+
|
|
76
|
+
readonly address: Readonly<Record<string, string>>
|
|
77
|
+
|
|
78
|
+
readonly columns: Readonly<Record<string, readonly string[]>>
|
|
79
|
+
|
|
80
|
+
readonly at: string
|
|
81
|
+
|
|
82
|
+
readonly migrations?: readonly { readonly name: string; readonly sql: string }[]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Stood {
|
|
86
|
+
|
|
87
|
+
readonly keeps: readonly string[]
|
|
88
|
+
|
|
89
|
+
readonly store: Store
|
|
90
|
+
|
|
91
|
+
seed(actor: Actor, rows: readonly { readonly entity: string; readonly row: Row }[]): Promise<void>
|
|
92
|
+
|
|
93
|
+
taken(actor: Actor): Promise<Readonly<Record<string, readonly Row[]>>>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function standing(held: Omit<Kept, 'at'>): Stood {
|
|
97
|
+
const store = storing({ ...held, at: 'memory://', migrations: [] })
|
|
98
|
+
const keeps = Object.keys(held.tables)
|
|
99
|
+
return {
|
|
100
|
+
keeps,
|
|
101
|
+
store,
|
|
102
|
+
async seed(actor, rows) {
|
|
103
|
+
for (const one of rows) await store.insert(actor, one.entity, one.row)
|
|
104
|
+
},
|
|
105
|
+
async taken(actor) {
|
|
106
|
+
const found: Record<string, readonly Row[]> = {}
|
|
107
|
+
for (const entity of keeps) found[entity] = await store.list(actor, entity)
|
|
108
|
+
return found
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function storing(held: Kept): Store {
|
|
114
|
+
const q = (name: string): string => JSON.stringify(name)
|
|
115
|
+
const rowOf = (one: unknown): Row => one as Row
|
|
116
|
+
const known = (entity: string): readonly string[] => held.columns[entity] ?? []
|
|
117
|
+
|
|
118
|
+
const tableFor = (entity: string): string => {
|
|
119
|
+
const found = held.tables[entity]
|
|
120
|
+
if (found === undefined) throw new Error(entity + ' is not kept in ' + held.name + ', so it has no table here')
|
|
121
|
+
return found
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const addressOf = (entity: string): string => {
|
|
125
|
+
const found = held.address[entity]
|
|
126
|
+
if (found === undefined) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
entity + ' declares nothing that identifies one of its rows, so a row of it cannot be addressed here. ' +
|
|
129
|
+
'Give it a value and say so with `thread create entity edit`.',
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
return found
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let opening: Promise<Queried> | undefined
|
|
136
|
+
|
|
137
|
+
const db = async (): Promise<Queried> => {
|
|
138
|
+
opening ??= (async () => {
|
|
139
|
+
const made = await opened(held.at)
|
|
140
|
+
await caughtUp(made, held.migrations ?? [])
|
|
141
|
+
await made.exec(held.schema)
|
|
142
|
+
return made
|
|
143
|
+
})()
|
|
144
|
+
return await opening
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
async insert(_actor, entity, row) {
|
|
149
|
+
const cols = known(entity)
|
|
150
|
+
const taken = cols.filter((one) => (row as Record<string, unknown>)[one] !== undefined)
|
|
151
|
+
const answered = await (await db()).query(
|
|
152
|
+
'insert into ' + q(tableFor(entity)) + ' (' + taken.map(q).join(', ') + ') values (' +
|
|
153
|
+
taken.map((_, at) => '$' + String(at + 1)).join(', ') + ') returning *',
|
|
154
|
+
taken.map((one) => (row as Record<string, unknown>)[one]),
|
|
155
|
+
)
|
|
156
|
+
return rowOf(answered.rows[0])
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
async get(_actor, entity, key, fields: FieldList) {
|
|
160
|
+
const cols = known(entity)
|
|
161
|
+
const asked = fields.filter((one) => cols.includes(one as string)).map((one) => String(one))
|
|
162
|
+
const answered = await (await db()).query(
|
|
163
|
+
'select ' + (asked.length === 0 ? '*' : asked.map(q).join(', ')) + ' from ' + q(tableFor(entity)) +
|
|
164
|
+
' where ' + q(addressOf(entity)) + ' = $1 limit 1',
|
|
165
|
+
[key],
|
|
166
|
+
)
|
|
167
|
+
return answered.rows.length === 0 ? null : rowOf(answered.rows[0])
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
async list(_actor, entity, opts) {
|
|
171
|
+
const cols = known(entity)
|
|
172
|
+
const asked = Object.entries(opts?.where ?? {}).filter(([one]) => cols.includes(one))
|
|
173
|
+
const answered = await (await db()).query(
|
|
174
|
+
'select * from ' + q(tableFor(entity)) +
|
|
175
|
+
(asked.length === 0 ? '' : ' where ' + asked.map(([one], at) => q(one) + ' = $' + String(at + 1)).join(' and ')) +
|
|
176
|
+
(opts?.limit === undefined ? '' : ' limit ' + String(opts.limit)),
|
|
177
|
+
asked.map(([, one]) => one),
|
|
178
|
+
)
|
|
179
|
+
return answered.rows.map(rowOf)
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
async update(_actor, entity, key, patch, expectedVersion) {
|
|
183
|
+
if (expectedVersion !== undefined) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
held.name + ' keeps what ' + entity + ' declares and nothing else, so there is no version on a row to ' +
|
|
186
|
+
'check an expected one against. Declare a value for it and compare that, or update without a version.',
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
const cols = known(entity)
|
|
190
|
+
const taken = Object.entries(patch).filter(([one]) => cols.includes(one))
|
|
191
|
+
if (taken.length === 0) throw new Error('nothing in this patch is a value ' + entity + ' declares')
|
|
192
|
+
const answered = await (await db()).query(
|
|
193
|
+
'update ' + q(tableFor(entity)) + ' set ' +
|
|
194
|
+
taken.map(([one], at) => q(one) + ' = $' + String(at + 1)).join(', ') +
|
|
195
|
+
' where ' + q(addressOf(entity)) + ' = $' + String(taken.length + 1) + ' returning *',
|
|
196
|
+
[...taken.map(([, one]) => one), key],
|
|
197
|
+
)
|
|
198
|
+
if (answered.rows.length === 0) throw new Error('record ' + String(key) + ' not found')
|
|
199
|
+
return rowOf(answered.rows[0])
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
async remove(_actor, entity, key) {
|
|
203
|
+
await (await db()).query('delete from ' + q(tableFor(entity)) + ' where ' + q(addressOf(entity)) + ' = $1', [key])
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { Member } from '../core/thread.ts';
|
|
2
2
|
export declare const SECRECY: {
|
|
3
|
-
readonly server:
|
|
4
|
-
readonly public: "pub_";
|
|
3
|
+
readonly server: 'sec_';
|
|
5
4
|
};
|
|
6
5
|
export declare const SAID: Readonly<Record<keyof typeof SECRECY, string>>;
|
|
6
|
+
export interface Vault {
|
|
7
|
+
resolve(refs: readonly string[]): Promise<Readonly<Record<string, string>>>;
|
|
8
|
+
}
|
|
7
9
|
export interface Secret extends Member {
|
|
8
10
|
readonly sends?: {
|
|
9
11
|
readonly header: string;
|
|
10
12
|
readonly prefix?: string;
|
|
11
13
|
};
|
|
14
|
+
readonly signs?: {
|
|
15
|
+
readonly header: string;
|
|
16
|
+
readonly prefix?: string;
|
|
17
|
+
};
|
|
12
18
|
}
|
|
13
19
|
export declare function secrecyOf(id: string): keyof typeof SECRECY | undefined;
|
|
14
20
|
export declare const isServerOnly: (id: string) => boolean;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
export const SECRECY = {
|
|
2
2
|
server: 'sec_',
|
|
3
|
-
public: 'pub_',
|
|
4
3
|
};
|
|
5
4
|
export const SAID = {
|
|
6
|
-
server: 'A value only a server may ever hold
|
|
7
|
-
public: 'A value that may reach a browser: a base url, a publishable key, a project id. It is still a secret and still lives in the sealed store, because it still differs between a sandbox and production and nothing else here keeps that difference. What it is not is a thing that has to be withheld.',
|
|
5
|
+
server: 'A value only a server may ever hold: an API key, a connection string, a signing key. Every secret is one of these. A value anybody may read is not a secret and is never sealed — the address of a place this app calls and a header every call carries are written on the target row, and only a credential goes into the vault. Everything a browser bundle reaches is in the bundle and belongs to whoever opens the page, so a target requiring a secret is refused a browser entry when it is built rather than when somebody reads it.',
|
|
8
6
|
};
|
|
9
7
|
export function secrecyOf(id) {
|
|
10
8
|
return Object.keys(SECRECY).find((one) => id.startsWith(SECRECY[one]));
|
|
@@ -12,8 +10,8 @@ export function secrecyOf(id) {
|
|
|
12
10
|
export const isServerOnly = (id) => secrecyOf(id) === 'server';
|
|
13
11
|
export const unclassified = (secrets) => secrets
|
|
14
12
|
.filter((one) => secrecyOf(one.id) === undefined)
|
|
15
|
-
.map((one) =>
|
|
16
|
-
'
|
|
13
|
+
.map((one) => one.id + ' does not wear ' + SECRECY.server + ', and every secret does: that prefix says only a server may ever hold the value. ' +
|
|
14
|
+
'A value anybody may read is not a secret. Write it on the row that needs it, as the address of a target or a header every call carries, and take this row out');
|
|
17
15
|
const VALUED = ['value', 'secret', 'key', 'token', 'password', 'env'];
|
|
18
16
|
export const valued = (secrets) => {
|
|
19
17
|
const found = [];
|
|
@@ -2,19 +2,23 @@ import type { Member } from '../core/thread.ts'
|
|
|
2
2
|
|
|
3
3
|
export const SECRECY = {
|
|
4
4
|
server: 'sec_',
|
|
5
|
-
public: 'pub_',
|
|
6
5
|
} as const satisfies Readonly<Record<string, string>>
|
|
7
6
|
|
|
8
7
|
export const SAID: Readonly<Record<keyof typeof SECRECY, string>> = {
|
|
9
8
|
server:
|
|
10
|
-
'A value only a server may ever hold
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
'A value only a server may ever hold: an API key, a connection string, a signing key. Every secret is one of these. A value anybody may read is not a secret and is never sealed — the address of a place this app calls and a header every call carries are written on the target row, and only a credential goes into the vault. Everything a browser bundle reaches is in the bundle and belongs to whoever opens the page, so a target requiring a secret is refused a browser entry when it is built rather than when somebody reads it.',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Vault {
|
|
13
|
+
|
|
14
|
+
resolve(refs: readonly string[]): Promise<Readonly<Record<string, string>>>
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export interface Secret extends Member {
|
|
16
18
|
|
|
17
19
|
readonly sends?: { readonly header: string; readonly prefix?: string }
|
|
20
|
+
|
|
21
|
+
readonly signs?: { readonly header: string; readonly prefix?: string }
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export function secrecyOf(id: string): keyof typeof SECRECY | undefined {
|
|
@@ -28,8 +32,8 @@ export const unclassified = (secrets: readonly Secret[]): readonly string[] =>
|
|
|
28
32
|
.filter((one) => secrecyOf(one.id) === undefined)
|
|
29
33
|
.map(
|
|
30
34
|
(one) =>
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
one.id + ' does not wear ' + SECRECY.server + ', and every secret does: that prefix says only a server may ever hold the value. ' +
|
|
36
|
+
'A value anybody may read is not a secret. Write it on the row that needs it, as the address of a target or a header every call carries, and take this row out',
|
|
33
37
|
)
|
|
34
38
|
|
|
35
39
|
const VALUED = ['value', 'secret', 'key', 'token', 'password', 'env']
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import type { Member } from '../core/thread.ts';
|
|
2
|
+
import type { Actor } from '../core/actor.ts';
|
|
2
3
|
import { type Secret } from './secret.ts';
|
|
3
4
|
export declare const REACH: {
|
|
4
|
-
readonly postgres:
|
|
5
|
-
readonly pglite:
|
|
6
|
-
readonly browser:
|
|
7
|
-
readonly https:
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
5
|
+
readonly postgres: 'pg_';
|
|
6
|
+
readonly pglite: 'pl_';
|
|
7
|
+
readonly browser: 'zu_';
|
|
8
|
+
readonly https: 'ht_';
|
|
9
|
+
readonly cli: 'cli_';
|
|
10
|
+
readonly disk: 'dsk_';
|
|
11
|
+
readonly sdk: 'sdk_';
|
|
10
12
|
};
|
|
11
13
|
export type Reach = keyof typeof REACH;
|
|
14
|
+
export declare const ADDRESS: {
|
|
15
|
+
readonly row: 'reached at the address written on its row, which anybody may read';
|
|
16
|
+
readonly secret: 'reached at a secret, because its address is a connection string and a connection string carries a password';
|
|
17
|
+
readonly nothing: 'addressed by nothing';
|
|
18
|
+
};
|
|
19
|
+
export type Addressed = keyof typeof ADDRESS;
|
|
12
20
|
export interface Holds {
|
|
13
|
-
readonly takes: 'entities' | 'calls' |
|
|
21
|
+
readonly takes: 'entities' | 'calls' | null;
|
|
14
22
|
readonly from: 'server' | 'browser' | 'either';
|
|
15
|
-
readonly addressed:
|
|
16
|
-
readonly arrives: 'npm' | 'machine' | null;
|
|
23
|
+
readonly addressed: Addressed;
|
|
17
24
|
readonly migrations: boolean;
|
|
18
25
|
readonly references: boolean;
|
|
19
26
|
readonly sql: boolean;
|
|
@@ -21,31 +28,165 @@ export interface Holds {
|
|
|
21
28
|
}
|
|
22
29
|
export declare const HOLDS: Readonly<Record<Reach, Holds>>;
|
|
23
30
|
export declare const SAID: Readonly<Record<Reach, string>>;
|
|
31
|
+
export declare const OPENS: Readonly<Record<Reach, readonly string[]>>;
|
|
32
|
+
export type Kind = 'file' | 'folder' | 'other' | 'none';
|
|
33
|
+
export interface Listed {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly kind: Exclude<Kind, 'none'>;
|
|
36
|
+
}
|
|
37
|
+
export interface Measured {
|
|
38
|
+
readonly size: number;
|
|
39
|
+
readonly modified: number;
|
|
40
|
+
}
|
|
41
|
+
export interface Answering {
|
|
42
|
+
readonly kind: (at: string) => Kind;
|
|
43
|
+
readonly read: (at: string) => string;
|
|
44
|
+
readonly write: (at: string, said: string, mode?: number) => void;
|
|
45
|
+
readonly list: (at: string) => readonly Listed[];
|
|
46
|
+
readonly stat: (at: string) => Measured;
|
|
47
|
+
readonly remove: (at: string) => void;
|
|
48
|
+
readonly real: (at: string) => string;
|
|
49
|
+
}
|
|
50
|
+
export interface Attached extends Answering {
|
|
51
|
+
readonly fetch: (at: string) => string;
|
|
52
|
+
readonly run: (at: string, words: readonly string[]) => Ran;
|
|
53
|
+
}
|
|
54
|
+
export declare const ORDERS: {
|
|
55
|
+
readonly kind: 'kind';
|
|
56
|
+
readonly read: 'read';
|
|
57
|
+
readonly write: 'written';
|
|
58
|
+
readonly list: 'list';
|
|
59
|
+
readonly stat: 'stat';
|
|
60
|
+
readonly remove: 'removed';
|
|
61
|
+
readonly real: 'real';
|
|
62
|
+
readonly fetch: 'fetched';
|
|
63
|
+
readonly run: 'ran';
|
|
64
|
+
};
|
|
65
|
+
export interface Order {
|
|
66
|
+
readonly order: keyof typeof ORDERS;
|
|
67
|
+
readonly at: string;
|
|
68
|
+
readonly said?: string;
|
|
69
|
+
readonly mode?: number;
|
|
70
|
+
readonly words?: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
export interface Answer {
|
|
73
|
+
readonly answered: string;
|
|
74
|
+
readonly found?: unknown;
|
|
75
|
+
readonly said?: string;
|
|
76
|
+
readonly at?: string;
|
|
77
|
+
readonly because?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface Recorded {
|
|
80
|
+
readonly order: Order;
|
|
81
|
+
readonly answered: Answer;
|
|
82
|
+
}
|
|
83
|
+
export interface How {
|
|
84
|
+
readonly at?: string;
|
|
85
|
+
readonly hears?: string;
|
|
86
|
+
readonly of?: string;
|
|
87
|
+
readonly answered?: readonly Answer[];
|
|
88
|
+
readonly recording?: readonly Recorded[];
|
|
89
|
+
readonly answering?: number;
|
|
90
|
+
}
|
|
91
|
+
export interface Ran {
|
|
92
|
+
readonly there: boolean;
|
|
93
|
+
readonly ok: boolean;
|
|
94
|
+
readonly code: number;
|
|
95
|
+
readonly out: string;
|
|
96
|
+
readonly err: string;
|
|
97
|
+
readonly killed: string | null;
|
|
98
|
+
readonly why: string;
|
|
99
|
+
readonly of?: string;
|
|
100
|
+
readonly asks?: readonly Order[];
|
|
101
|
+
readonly lost?: string;
|
|
102
|
+
}
|
|
103
|
+
export type Running<C extends string = string> = Readonly<Record<C, (args: readonly string[], shows?: boolean | 'errors' | 'tee', within?: number, how?: How) => Promise<Ran>>>;
|
|
104
|
+
export type Calling<C extends string = string> = {
|
|
105
|
+
readonly [K in C]: (given?: unknown) => Promise<unknown>;
|
|
106
|
+
};
|
|
107
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
108
|
+
export interface HttpRequest {
|
|
109
|
+
readonly target: string;
|
|
110
|
+
readonly address: string;
|
|
111
|
+
readonly method: HttpMethod;
|
|
112
|
+
readonly path: string;
|
|
113
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
114
|
+
readonly body?: Uint8Array;
|
|
115
|
+
readonly contentType?: string;
|
|
116
|
+
readonly idempotencyKey?: string;
|
|
117
|
+
readonly told?: (said: unknown) => void;
|
|
118
|
+
}
|
|
119
|
+
export interface HttpResponse {
|
|
120
|
+
readonly status: number;
|
|
121
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
122
|
+
readonly body: Uint8Array;
|
|
123
|
+
}
|
|
124
|
+
export interface Http {
|
|
125
|
+
call(request: HttpRequest, within?: number): Promise<HttpResponse>;
|
|
126
|
+
}
|
|
127
|
+
export type Row = Record<string, unknown>;
|
|
128
|
+
export type PropertyValue = string | number | boolean | null | readonly (string | number | boolean)[];
|
|
129
|
+
export declare function isRow(v: unknown): v is Row;
|
|
130
|
+
export type ReadField = `fld_${string}` | `rel_${string}` | (string & {});
|
|
131
|
+
export type FieldList = readonly [ReadField, ...ReadField[]];
|
|
132
|
+
export interface Store {
|
|
133
|
+
insert(actor: Actor, entity: string, row: Row): Promise<Row>;
|
|
134
|
+
get(actor: Actor, entity: string, key: string | number, fields: FieldList): Promise<Row | null>;
|
|
135
|
+
list(actor: Actor, entity: string, opts?: {
|
|
136
|
+
limit?: number;
|
|
137
|
+
where?: Readonly<Record<string, string | number | boolean | null>>;
|
|
138
|
+
}): Promise<Row[]>;
|
|
139
|
+
update(actor: Actor, entity: string, key: string | number, patch: Record<string, unknown>, expectedVersion?: number): Promise<Row>;
|
|
140
|
+
remove(actor: Actor, entity: string, key: string | number): Promise<void>;
|
|
141
|
+
}
|
|
142
|
+
export interface Transactional {
|
|
143
|
+
transaction<T>(actor: Actor, fn: () => Promise<T>): Promise<T>;
|
|
144
|
+
}
|
|
145
|
+
export declare function isTransactional(store: Store): store is Store & Transactional;
|
|
146
|
+
export interface Integration extends Member {
|
|
147
|
+
readonly needs?: Readonly<Record<string, string>>;
|
|
148
|
+
}
|
|
24
149
|
export interface Target extends Member {
|
|
150
|
+
readonly of: string;
|
|
25
151
|
readonly takes: readonly string[];
|
|
26
152
|
readonly requires: readonly string[];
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
153
|
+
readonly address?: string;
|
|
154
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
155
|
+
}
|
|
156
|
+
export interface Webhook extends Member {
|
|
157
|
+
readonly of: string;
|
|
158
|
+
readonly path: string;
|
|
159
|
+
readonly arrives?: string;
|
|
160
|
+
readonly invokes: string;
|
|
29
161
|
}
|
|
30
162
|
export interface Call extends Member {
|
|
31
163
|
readonly of: string;
|
|
32
|
-
readonly method
|
|
164
|
+
readonly method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
33
165
|
readonly path: string;
|
|
34
166
|
}
|
|
35
167
|
export declare const CALL_PREFIX = "cl_";
|
|
36
168
|
export declare const fills: (one: Call) => readonly string[];
|
|
37
169
|
export declare function reachOf(id: string): Reach | undefined;
|
|
170
|
+
export declare const standsHere: (one: Target, targets: readonly Target[]) => boolean;
|
|
38
171
|
export declare function holdsOf(id: string): Holds | undefined;
|
|
172
|
+
export declare function requirable(targets: readonly Target[], calls: readonly Call[]): readonly Target[];
|
|
39
173
|
export declare function driverOf(id: string): string | null;
|
|
40
174
|
export declare function addressOf(one: Target, secrets: readonly Secret[]): Secret | undefined;
|
|
41
175
|
export declare function credentialsOf(one: Target, secrets: readonly Secret[]): readonly Secret[];
|
|
42
176
|
export declare function servesBrowser(one: Target, secrets: readonly Secret[]): true | string;
|
|
43
177
|
export declare const unreachable: (targets: readonly Target[]) => readonly string[];
|
|
178
|
+
export declare const LOCKFILES: Readonly<Record<string, string>>;
|
|
179
|
+
export declare const PACKAGER = "packager";
|
|
180
|
+
export declare const managerOf: (at: string, there: (path: string) => boolean) => string;
|
|
181
|
+
export declare function programOf(name: string, at: string, there: (path: string) => boolean): string | undefined;
|
|
182
|
+
export declare const unlisted: (name: string) => string;
|
|
44
183
|
export declare function misnamed(target: string, name: string): string | undefined;
|
|
45
184
|
export declare function misaccepted(targets: readonly Target[], entities: readonly string[], calls: readonly Call[]): readonly string[];
|
|
185
|
+
export declare const placedAt: (targets: readonly Target[], id: string) => string;
|
|
186
|
+
export declare function placed(one: Target): readonly string[];
|
|
46
187
|
export declare function unaddressed(targets: readonly Target[], secrets: readonly Secret[]): readonly string[];
|
|
47
188
|
export declare function unheld(targets: readonly Target[], secrets: readonly Secret[]): readonly string[];
|
|
48
189
|
export declare function unanswered(calls: readonly Call[], targets: readonly Target[]): readonly string[];
|
|
49
|
-
export declare function
|
|
50
|
-
export declare function refusals(targets: readonly Target[], secrets: readonly Secret[], calls: readonly Call[], entities: readonly string[]): readonly string[];
|
|
190
|
+
export declare function unplaced(targets: readonly Target[], integrations: readonly Integration[]): readonly string[];
|
|
191
|
+
export declare function refusals(targets: readonly Target[], secrets: readonly Secret[], calls: readonly Call[], entities: readonly string[], integrations: readonly Integration[]): readonly string[];
|
|
51
192
|
export declare function targetOf(entity: string, targets: readonly Target[]): Target | string;
|