@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,470 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
|
-
|
|
3
|
-
import type { Actor } from '../core/actor.ts'
|
|
4
|
-
import type {
|
|
5
|
-
Store, Row, Seams, Telemetry, Log, Audit, Errors, Search, Cost, Idempotency, EventBus, RateLimiter, PlatformEvent, LogLevel,
|
|
6
|
-
Queue, Job, EnqueueOpts, Transactional, FeatureGate, Quotas, Outbox, AuditRecord, ErrorRecord, Vault,
|
|
7
|
-
Files, Views, FieldList } from './seams.ts'
|
|
8
|
-
import { backoffMs, isExhausted } from './queue-policy.ts'
|
|
9
|
-
|
|
10
|
-
function projectRow(row: Row, fields: FieldList, key: string): Row {
|
|
11
|
-
const keep = new Set<string>([key, 'version', ...fields])
|
|
12
|
-
const projected: Row = {}
|
|
13
|
-
for (const [name, value] of Object.entries(row)) {
|
|
14
|
-
if (keep.has(name)) projected[name] = value
|
|
15
|
-
}
|
|
16
|
-
return projected
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class UnknownColumnError extends Error {
|
|
20
|
-
constructor(entity: string, key: string) {
|
|
21
|
-
super(
|
|
22
|
-
`${entity} does not declare "${key}", so writing it would be silently discarded. ` +
|
|
23
|
-
'Declare the field or relation on the entity, or stop writing the key.',
|
|
24
|
-
)
|
|
25
|
-
this.name = 'UnknownColumnError'
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class VersionConflictError extends Error {
|
|
30
|
-
constructor(entity: string, id: string, expectedVersion: number) {
|
|
31
|
-
super(
|
|
32
|
-
`${entity} ${id} was not at version ${expectedVersion} — someone else changed it first. ` +
|
|
33
|
-
're-read the row and retry with its current ETag.',
|
|
34
|
-
)
|
|
35
|
-
this.name = 'VersionConflictError'
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class MemoryStore implements Store, Transactional {
|
|
40
|
-
private data = new Map<string, Row[]>()
|
|
41
|
-
|
|
42
|
-
private readonly declared: ReadonlyMap<string, ReadonlySet<string>>
|
|
43
|
-
|
|
44
|
-
private readonly keys: ReadonlyMap<string, string>
|
|
45
|
-
|
|
46
|
-
constructor(declared?: ReadonlyMap<string, ReadonlySet<string>>, keys?: ReadonlyMap<string, string>) {
|
|
47
|
-
this.declared = declared ?? new Map()
|
|
48
|
-
this.keys = keys ?? new Map()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private keyOf(entity: string): string {
|
|
52
|
-
const held = this.keys.get(entity)
|
|
53
|
-
if (held === undefined) {
|
|
54
|
-
throw new Error(
|
|
55
|
-
`nothing told this store which value identifies a ${entity}, so it cannot find one row rather than all of ` +
|
|
56
|
-
'them. An entity states that with a `unique` invariant over one of its values, and whatever builds this ' +
|
|
57
|
-
'store passes those along with the columns each entity declares.',
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
return held
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private static readonly SYSTEM = new Set(['version'])
|
|
64
|
-
|
|
65
|
-
private assertDeclared(entity: string, row: Record<string, unknown>): void {
|
|
66
|
-
const allowed = this.declared.get(entity)
|
|
67
|
-
if (allowed === undefined) return
|
|
68
|
-
for (const key of Object.keys(row)) {
|
|
69
|
-
if (allowed.has(key) || MemoryStore.SYSTEM.has(key)) continue
|
|
70
|
-
throw new UnknownColumnError(entity, key)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
private depth = 0
|
|
75
|
-
|
|
76
|
-
async transaction<T>(_actor: Actor, fn: () => Promise<T>): Promise<T> {
|
|
77
|
-
if (this.depth > 0) return fn()
|
|
78
|
-
const snapshot = new Map<string, Row[]>()
|
|
79
|
-
for (const [key, rows] of this.data) snapshot.set(key, rows.map((r) => ({ ...r })))
|
|
80
|
-
|
|
81
|
-
this.depth += 1
|
|
82
|
-
try {
|
|
83
|
-
return await fn()
|
|
84
|
-
} catch (err) {
|
|
85
|
-
this.data = snapshot
|
|
86
|
-
throw err
|
|
87
|
-
} finally {
|
|
88
|
-
this.depth -= 1
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async insert(_actor: Actor, entity: string, row: Row): Promise<Row> {
|
|
93
|
-
this.assertDeclared(entity, row)
|
|
94
|
-
const rows = this.data.get(entity) ?? []
|
|
95
|
-
|
|
96
|
-
const rec = { ...row, version: 1 }
|
|
97
|
-
rows.push(rec)
|
|
98
|
-
this.data.set(entity, rows)
|
|
99
|
-
return { ...rec }
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async get(_actor: Actor, entity: string, key: string | number, fields: FieldList): Promise<Row | null> {
|
|
103
|
-
const named = this.keyOf(entity)
|
|
104
|
-
const row = this.data.get(entity)?.find((r) => r[named] === key) ?? null
|
|
105
|
-
return row === null ? null : projectRow(row, fields, named)
|
|
106
|
-
}
|
|
107
|
-
async list(
|
|
108
|
-
_actor: Actor,
|
|
109
|
-
entity: string,
|
|
110
|
-
opts?: { limit?: number; where?: Readonly<Record<string, string | number | boolean | null>> },
|
|
111
|
-
): Promise<Row[]> {
|
|
112
|
-
const all = this.data.get(entity) ?? []
|
|
113
|
-
|
|
114
|
-
const where = Object.entries(opts?.where ?? {})
|
|
115
|
-
for (const [field] of where) this.assertDeclared(entity, { [field]: null })
|
|
116
|
-
|
|
117
|
-
const matched = where.length === 0 ? all : all.filter((row) => where.every(([f, v]) => (row[f] ?? null) === v))
|
|
118
|
-
return opts?.limit ? matched.slice(0, opts.limit).map((r) => ({ ...r })) : matched.map((r) => ({ ...r }))
|
|
119
|
-
}
|
|
120
|
-
async update(
|
|
121
|
-
_actor: Actor,
|
|
122
|
-
entity: string,
|
|
123
|
-
key: string | number,
|
|
124
|
-
patch: Record<string, unknown>,
|
|
125
|
-
expectedVersion?: number,
|
|
126
|
-
): Promise<Row> {
|
|
127
|
-
this.assertDeclared(entity, patch)
|
|
128
|
-
const named = this.keyOf(entity)
|
|
129
|
-
const rows = this.data.get(entity) ?? []
|
|
130
|
-
const i = rows.findIndex((r) => r[named] === key)
|
|
131
|
-
const cur = rows[i]
|
|
132
|
-
if (i < 0 || !cur) throw new Error(`record ${String(key)} not found`)
|
|
133
|
-
const currentVersion = typeof cur['version'] === 'number' ? cur['version'] : 1
|
|
134
|
-
if (expectedVersion !== undefined && expectedVersion !== currentVersion) {
|
|
135
|
-
throw new VersionConflictError(entity, String(key), expectedVersion)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const next = { ...cur, ...patch, [named]: key, version: currentVersion + 1 }
|
|
139
|
-
rows[i] = next
|
|
140
|
-
return { ...next }
|
|
141
|
-
}
|
|
142
|
-
async remove(_actor: Actor, entity: string, key: string | number): Promise<void> {
|
|
143
|
-
const named = this.keyOf(entity)
|
|
144
|
-
this.data.set(entity, (this.data.get(entity) ?? []).filter((r) => r[named] !== key))
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export class MemoryQueue implements Queue {
|
|
149
|
-
private jobs = new Map<string, Job & { runAt: number; claimed: boolean; dedupeKey?: string }>()
|
|
150
|
-
private dead: Job[] = []
|
|
151
|
-
private seq = 0
|
|
152
|
-
|
|
153
|
-
async enqueue<T>(actor: Actor, kind: string, payload: T, opts?: EnqueueOpts): Promise<string> {
|
|
154
|
-
|
|
155
|
-
if (opts?.dedupeKey) {
|
|
156
|
-
for (const [id, job] of this.jobs) {
|
|
157
|
-
if (job.dedupeKey === opts.dedupeKey) return id
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const id = `job_${++this.seq}`
|
|
161
|
-
this.jobs.set(id, {
|
|
162
|
-
id,
|
|
163
|
-
kind,
|
|
164
|
-
payload,
|
|
165
|
-
attempts: 0,
|
|
166
|
-
runAt: opts?.runAt ?? 0,
|
|
167
|
-
claimed: false,
|
|
168
|
-
dedupeKey: opts?.dedupeKey,
|
|
169
|
-
actor,
|
|
170
|
-
})
|
|
171
|
-
return id
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async poll(kinds: string[], limit: number, now: number): Promise<Job[]> {
|
|
175
|
-
const due: Job[] = []
|
|
176
|
-
for (const job of this.jobs.values()) {
|
|
177
|
-
if (due.length >= limit) break
|
|
178
|
-
if (job.claimed || !kinds.includes(job.kind) || job.runAt > now) continue
|
|
179
|
-
job.claimed = true
|
|
180
|
-
due.push({ ...job })
|
|
181
|
-
}
|
|
182
|
-
return due
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async ack(id: string): Promise<void> {
|
|
186
|
-
this.jobs.delete(id)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async fail(id: string, error: string, now: number): Promise<void> {
|
|
190
|
-
const job = this.jobs.get(id)
|
|
191
|
-
if (!job) return
|
|
192
|
-
job.attempts += 1
|
|
193
|
-
job.claimed = false
|
|
194
|
-
|
|
195
|
-
if (isExhausted(job.attempts)) {
|
|
196
|
-
|
|
197
|
-
this.dead.push({ ...job })
|
|
198
|
-
this.jobs.delete(id)
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
job.runAt = now + backoffMs(job.attempts)
|
|
202
|
-
void error
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async deadLetter(id: string, error: string): Promise<void> {
|
|
206
|
-
const job = this.jobs.get(id)
|
|
207
|
-
if (!job) return
|
|
208
|
-
this.dead.push({ ...job })
|
|
209
|
-
this.jobs.delete(id)
|
|
210
|
-
void error
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async deadLetters(): Promise<Job[]> {
|
|
214
|
-
return this.dead.map((j) => ({ ...j }))
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export interface Sink {
|
|
219
|
-
logs: { level: LogLevel; action: string; data: Record<string, unknown> }[]
|
|
220
|
-
events: PlatformEvent[]
|
|
221
|
-
audits: AuditRecord[]
|
|
222
|
-
errors: ErrorRecord[]
|
|
223
|
-
costs: string[]
|
|
224
|
-
indexed: { entity: string; row: Row }[]
|
|
225
|
-
spans: { action: string; status: 'ok' | 'error' }[]
|
|
226
|
-
|
|
227
|
-
outbox: PlatformEvent[]
|
|
228
|
-
|
|
229
|
-
gateOrder: string[]
|
|
230
|
-
|
|
231
|
-
quota: { reserved: number[]; committed: number[]; released: number }
|
|
232
|
-
|
|
233
|
-
files: { action: 'put' | 'get' | 'signed' | 'remove'; key: string; bytes: number | null }[]
|
|
234
|
-
|
|
235
|
-
queries: { sql: string; params: readonly unknown[]; rows: number }[]
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export class MemoryIdempotency implements Idempotency {
|
|
239
|
-
private readonly records: Map<string, unknown>
|
|
240
|
-
private readonly onCheck?: () => void
|
|
241
|
-
|
|
242
|
-
constructor(opts: {
|
|
243
|
-
readonly onCheck?: () => void
|
|
244
|
-
|
|
245
|
-
readonly records?: Map<string, unknown>
|
|
246
|
-
}) {
|
|
247
|
-
this.records = opts.records ?? new Map<string, unknown>()
|
|
248
|
-
this.onCheck = opts.onCheck
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
private keyFor(key: string, action: string): string {
|
|
252
|
-
return `${key}:${action}`
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
async check(key: string | undefined, action: string): Promise<unknown | null> {
|
|
256
|
-
this.onCheck?.()
|
|
257
|
-
if (!key) return null
|
|
258
|
-
const k = this.keyFor(key, action)
|
|
259
|
-
return this.records.has(k) ? (this.records.get(k) ?? null) : null
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async store(key: string | undefined, action: string, output: unknown): Promise<void> {
|
|
263
|
-
if (!key) return
|
|
264
|
-
const k = this.keyFor(key, action)
|
|
265
|
-
|
|
266
|
-
if (!this.records.has(k)) this.records.set(k, output)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export interface MemorySeamOptions {
|
|
271
|
-
readonly allowRate?: boolean
|
|
272
|
-
|
|
273
|
-
readonly features?: readonly string[]
|
|
274
|
-
|
|
275
|
-
readonly quota?: Readonly<Record<string, number>>
|
|
276
|
-
readonly onExceed?: string
|
|
277
|
-
|
|
278
|
-
readonly entities?: ReadonlyMap<string, ReadonlySet<string>>
|
|
279
|
-
|
|
280
|
-
readonly keys?: ReadonlyMap<string, string>
|
|
281
|
-
|
|
282
|
-
readonly secrets?: Readonly<Record<string, string>>
|
|
283
|
-
|
|
284
|
-
readonly views?: Readonly<Record<string, readonly unknown[]>>
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function memoryVault(seed: Readonly<Record<string, string>> = {}): Vault {
|
|
288
|
-
return {
|
|
289
|
-
async resolve(refs) {
|
|
290
|
-
const found: Record<string, string> = {}
|
|
291
|
-
for (const ref of refs) {
|
|
292
|
-
const value = seed[ref]
|
|
293
|
-
if (value === undefined) throw new Error(`vault has no value for "${ref}"`)
|
|
294
|
-
found[ref] = value
|
|
295
|
-
}
|
|
296
|
-
return found
|
|
297
|
-
},
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export const arrived = (): number => Date.now()
|
|
302
|
-
|
|
303
|
-
export const fresh = (): string => randomUUID()
|
|
304
|
-
|
|
305
|
-
export function inMemorySeams(opts?: MemorySeamOptions): {
|
|
306
|
-
seams: Seams
|
|
307
|
-
sink: Sink
|
|
308
|
-
store: MemoryStore
|
|
309
|
-
queue: MemoryQueue
|
|
310
|
-
} {
|
|
311
|
-
const sink: Sink = {
|
|
312
|
-
logs: [], events: [], audits: [], errors: [], costs: [], indexed: [], spans: [],
|
|
313
|
-
outbox: [], gateOrder: [], quota: { reserved: [], committed: [], released: 0 },
|
|
314
|
-
files: [], queries: [],
|
|
315
|
-
}
|
|
316
|
-
const store = new MemoryStore(opts?.entities, opts?.keys)
|
|
317
|
-
const queue = new MemoryQueue()
|
|
318
|
-
const searchIdx = new Map<string, Row[]>()
|
|
319
|
-
|
|
320
|
-
const telemetry: Telemetry = {
|
|
321
|
-
start(action) {
|
|
322
|
-
let status: 'ok' | 'error' = 'ok'
|
|
323
|
-
return {
|
|
324
|
-
ok() { status = 'ok' },
|
|
325
|
-
error() { status = 'error' },
|
|
326
|
-
end() { sink.spans.push({ action, status }) },
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
}
|
|
330
|
-
const log: Log = { write(level, action, _actor, data) { sink.logs.push({ level, action, data }) } }
|
|
331
|
-
|
|
332
|
-
const audit: Audit = { async append(_actor, record) { sink.audits.push(record) } }
|
|
333
|
-
const errors: Errors = { async record(_actor, record) { sink.errors.push(record) } }
|
|
334
|
-
const cost: Cost = { meter(action) { sink.costs.push(action) } }
|
|
335
|
-
const events: EventBus = { async publish(e) { sink.events.push(e) } }
|
|
336
|
-
const rateLimiter: RateLimiter = {
|
|
337
|
-
async check() {
|
|
338
|
-
sink.gateOrder.push('rate')
|
|
339
|
-
return opts?.allowRate ?? true
|
|
340
|
-
},
|
|
341
|
-
async checkPolicy() {
|
|
342
|
-
sink.gateOrder.push('rate')
|
|
343
|
-
return opts?.allowRate ?? true
|
|
344
|
-
},
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const featureGate: FeatureGate | undefined =
|
|
348
|
-
opts?.features === undefined
|
|
349
|
-
? undefined
|
|
350
|
-
: {
|
|
351
|
-
async check(_actor, feature) {
|
|
352
|
-
sink.gateOrder.push('feature')
|
|
353
|
-
return (opts.features ?? []).includes(feature)
|
|
354
|
-
},
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const used = new Map<string, number>()
|
|
358
|
-
const quotas: Quotas | undefined =
|
|
359
|
-
opts?.quota === undefined
|
|
360
|
-
? undefined
|
|
361
|
-
: {
|
|
362
|
-
async reserve(_actor, meter, estimate) {
|
|
363
|
-
sink.gateOrder.push('quota')
|
|
364
|
-
sink.quota.reserved.push(estimate)
|
|
365
|
-
|
|
366
|
-
const allowance = opts.quota?.[meter] ?? Number.POSITIVE_INFINITY
|
|
367
|
-
const before = used.get(meter) ?? 0
|
|
368
|
-
|
|
369
|
-
if (before + estimate > allowance) {
|
|
370
|
-
|
|
371
|
-
return {
|
|
372
|
-
ok: false,
|
|
373
|
-
behavior: opts.onExceed ?? 'block',
|
|
374
|
-
retryAfterSeconds: 60,
|
|
375
|
-
async commit() {},
|
|
376
|
-
async release() {},
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
used.set(meter, before + estimate)
|
|
381
|
-
let settled = false
|
|
382
|
-
return {
|
|
383
|
-
ok: true,
|
|
384
|
-
behavior: null,
|
|
385
|
-
retryAfterSeconds: 0,
|
|
386
|
-
async commit(actual) {
|
|
387
|
-
if (settled) return
|
|
388
|
-
settled = true
|
|
389
|
-
sink.quota.committed.push(actual)
|
|
390
|
-
used.set(meter, (used.get(meter) ?? 0) - estimate + actual)
|
|
391
|
-
},
|
|
392
|
-
async release() {
|
|
393
|
-
if (settled) return
|
|
394
|
-
settled = true
|
|
395
|
-
sink.quota.released += 1
|
|
396
|
-
used.set(meter, (used.get(meter) ?? 0) - estimate)
|
|
397
|
-
},
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const outbox: Outbox = {
|
|
403
|
-
async emit(_actor, event) {
|
|
404
|
-
sink.outbox.push(event)
|
|
405
|
-
},
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const idempotency: Idempotency = new MemoryIdempotency({
|
|
409
|
-
onCheck: () => sink.gateOrder.push('idempotency'),
|
|
410
|
-
})
|
|
411
|
-
const search: Search = {
|
|
412
|
-
async index(_actor, entity, row) {
|
|
413
|
-
sink.indexed.push({ entity, row })
|
|
414
|
-
searchIdx.set(entity, [...(searchIdx.get(entity) ?? []), row])
|
|
415
|
-
},
|
|
416
|
-
async query(_actor, entity, q) {
|
|
417
|
-
|
|
418
|
-
const needle = q.toLowerCase()
|
|
419
|
-
return (searchIdx.get(entity) ?? []).filter((r) => JSON.stringify(r).toLowerCase().includes(needle))
|
|
420
|
-
},
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const vault: Vault | undefined = opts?.secrets === undefined ? undefined : memoryVault(opts.secrets)
|
|
424
|
-
|
|
425
|
-
const stored = new Map<string, { body: Uint8Array; contentType: string }>()
|
|
426
|
-
const files: Files = {
|
|
427
|
-
async put(_actor, key, body, contentType) {
|
|
428
|
-
stored.set(key, { body, contentType })
|
|
429
|
-
sink.files.push({ action: 'put', key, bytes: body.byteLength })
|
|
430
|
-
return { size: body.byteLength }
|
|
431
|
-
},
|
|
432
|
-
async get(_actor, key) {
|
|
433
|
-
const found = stored.get(key)
|
|
434
|
-
sink.files.push({ action: 'get', key, bytes: found === undefined ? null : found.body.byteLength })
|
|
435
|
-
return found === undefined ? null : { body: found.body, contentType: found.contentType }
|
|
436
|
-
},
|
|
437
|
-
async signedUrl(_actor, key, expiresInSeconds) {
|
|
438
|
-
sink.files.push({ action: 'signed', key, bytes: null })
|
|
439
|
-
return `https://files.invalid/unsigned-in-memory/${encodeURIComponent(key)}?expires_in=${String(expiresInSeconds)}`
|
|
440
|
-
},
|
|
441
|
-
async remove(_actor, key) {
|
|
442
|
-
stored.delete(key)
|
|
443
|
-
sink.files.push({ action: 'remove', key, bytes: null })
|
|
444
|
-
},
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const answers = opts?.views
|
|
448
|
-
const views: Views | undefined =
|
|
449
|
-
answers === undefined
|
|
450
|
-
? undefined
|
|
451
|
-
: {
|
|
452
|
-
async query<T>(_actor: Actor, sql: string, params: readonly unknown[]) {
|
|
453
|
-
const found = (answers[sql] ?? []) as readonly T[]
|
|
454
|
-
sink.queries.push({ sql, params, rows: found.length })
|
|
455
|
-
return found
|
|
456
|
-
},
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
seams: {
|
|
461
|
-
store, telemetry, log, audit, errors, search, cost, idempotency, events, rateLimiter, queue,
|
|
462
|
-
outbox, files, clock: { now: arrived }, random: { next: Math.random, uuid: fresh },
|
|
463
|
-
...(featureGate ? { featureGate } : {}),
|
|
464
|
-
...(quotas ? { quotas } : {}),
|
|
465
|
-
...(vault ? { vault } : {}),
|
|
466
|
-
...(views ? { views } : {}),
|
|
467
|
-
},
|
|
468
|
-
sink, store, queue,
|
|
469
|
-
}
|
|
470
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { PlatformError } from './errors/catalog.ts';
|
|
2
|
-
import type { PropertyValue, Search, Seams, Store, Views } from './seams.ts';
|
|
3
|
-
export interface ActionBudget {
|
|
4
|
-
readonly roundTrips: number;
|
|
5
|
-
}
|
|
6
|
-
export interface WriteAmplification {
|
|
7
|
-
readonly entity: string;
|
|
8
|
-
readonly id: string;
|
|
9
|
-
readonly columns: readonly string[];
|
|
10
|
-
readonly bytes: number;
|
|
11
|
-
}
|
|
12
|
-
export interface DatabaseUsage {
|
|
13
|
-
readonly roundTrips: number;
|
|
14
|
-
readonly rowsRead: number;
|
|
15
|
-
readonly rowsWritten: number;
|
|
16
|
-
readonly bytesRead: number;
|
|
17
|
-
readonly bytesWritten: number;
|
|
18
|
-
readonly amplified: readonly WriteAmplification[];
|
|
19
|
-
}
|
|
20
|
-
export declare class RoundTripBudgetExceededError extends PlatformError {
|
|
21
|
-
readonly budget: number;
|
|
22
|
-
readonly refusedStatement: string;
|
|
23
|
-
constructor(budget: number, statement: string);
|
|
24
|
-
}
|
|
25
|
-
export declare const BUDGET_EXCEEDED_BECAUSE = "round_trip_budget_exceeded";
|
|
26
|
-
export declare const DB_USAGE_PROPERTY = "db";
|
|
27
|
-
export declare const DB_USAGE_ATTRIBUTES: {
|
|
28
|
-
readonly roundTrips: "thread.db.round_trips";
|
|
29
|
-
readonly rowsRead: "thread.db.rows_read";
|
|
30
|
-
readonly rowsWritten: "thread.db.rows_written";
|
|
31
|
-
readonly bytesRead: "thread.db.bytes_read";
|
|
32
|
-
readonly bytesWritten: "thread.db.bytes_written";
|
|
33
|
-
readonly budgetRoundTrips: "thread.db.budget_round_trips";
|
|
34
|
-
};
|
|
35
|
-
export declare const DB_AMPLIFICATION_PROPERTY = "db_amplification";
|
|
36
|
-
export declare const DB_AMPLIFICATION_ATTRIBUTES: {
|
|
37
|
-
readonly entities: "thread.db.amplified_entities";
|
|
38
|
-
readonly columns: "thread.db.amplified_columns";
|
|
39
|
-
readonly bytes: "thread.db.amplified_bytes";
|
|
40
|
-
readonly writes: "thread.db.amplified_writes";
|
|
41
|
-
};
|
|
42
|
-
export declare function databaseUsageValues(usage: DatabaseUsage, budget?: ActionBudget): Readonly<Record<string, PropertyValue>>;
|
|
43
|
-
export declare function writeAmplificationValues(usage: DatabaseUsage): Readonly<Record<string, PropertyValue>> | undefined;
|
|
44
|
-
export declare function payloadBytes(value: unknown, depth?: number): number;
|
|
45
|
-
export interface MeterableSeams {
|
|
46
|
-
readonly store?: Store;
|
|
47
|
-
readonly search?: Search;
|
|
48
|
-
readonly views?: Views;
|
|
49
|
-
}
|
|
50
|
-
export interface Metered<S> {
|
|
51
|
-
readonly seams: S;
|
|
52
|
-
usage(): DatabaseUsage;
|
|
53
|
-
}
|
|
54
|
-
export type MeteredSeams = Metered<Seams>;
|
|
55
|
-
export declare function meterDatabase<S extends MeterableSeams>(seams: S, budget?: ActionBudget): Metered<S>;
|