@prestack/thread 0.1.20 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +193 -0
- package/dist/core/attach.js +21 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +18 -3
- package/dist/core/generators/commit.js +55 -3
- package/dist/core/generators/dev.d.ts +1 -0
- package/dist/core/generators/dev.js +68 -0
- package/dist/core/rows.d.ts +5 -0
- package/dist/core/rows.js +45 -0
- package/dist/core/tree.d.ts +1 -0
- package/dist/core/tree.js +7 -1
- package/dist/derivation/generators/build.d.ts +3 -1
- package/dist/derivation/generators/build.js +103 -22
- package/dist/derivation/generators/release.js +74 -26
- package/dist/derivation/release.js +3 -3
- package/dist/integrations/cli.d.ts +3 -9
- package/dist/integrations/cli.js +96 -36
- package/dist/integrations/declared.d.ts +8 -0
- package/dist/integrations/declared.js +22 -0
- package/dist/integrations/pglite.js +13 -3
- package/dist/integrations/target.d.ts +4 -1
- package/dist/integrations/target.js +20 -6
- package/dist/integrity/attach.js +1 -5
- package/dist/integrity/generators/publish.js +2 -1
- package/dist/integrity/slopnators/sl_stale_path.js +10 -0
- package/dist/integrity/slopnators/sl_unlisted_program.js +2 -2
- package/dist/operations/access.js +3 -2
- package/dist/operations/capabilities.d.ts +2 -0
- package/dist/operations/capabilities.js +2 -0
- package/dist/operations/generators/capability.js +234 -24
- package/dist/operations/generators/compile.d.ts +21 -0
- package/dist/operations/generators/compile.js +151 -51
- package/dist/operations/generators/permits.d.ts +2 -1
- package/dist/operations/generators/permits.js +52 -47
- package/dist/operations/generators/seed.d.ts +3 -0
- package/dist/operations/generators/seed.js +25 -3
- package/dist/operations/nodes.d.ts +2 -2
- package/package.json +1 -1
- package/dist/core/actor.ts +0 -21
- package/dist/core/attach.ts +0 -613
- package/dist/core/chain.ts +0 -145
- package/dist/core/condition.ts +0 -227
- package/dist/core/descend.ts +0 -1173
- package/dist/core/errors/catalogue.ts +0 -314
- package/dist/core/errors/classes.ts +0 -51
- package/dist/core/errors/error.ts +0 -152
- package/dist/core/errors/namespaces.ts +0 -19
- package/dist/core/finding.ts +0 -37
- package/dist/core/generators/commit.ts +0 -157
- package/dist/core/init.ts +0 -222
- package/dist/core/invoked.ts +0 -52
- package/dist/core/round.ts +0 -74
- package/dist/core/rows.ts +0 -459
- package/dist/core/scope.ts +0 -20
- package/dist/core/thread.ts +0 -29
- package/dist/core/tree.ts +0 -90
- package/dist/core/types.ts +0 -351
- package/dist/derivation/attach.ts +0 -57
- package/dist/derivation/generators/build.ts +0 -371
- package/dist/derivation/generators/check.ts +0 -118
- package/dist/derivation/generators/determinism.ts +0 -145
- package/dist/derivation/generators/release.ts +0 -236
- package/dist/derivation/release.ts +0 -43
- package/dist/integrations/attach.ts +0 -187
- package/dist/integrations/cli.ts +0 -615
- package/dist/integrations/disk.ts +0 -466
- package/dist/integrations/generators/read.ts +0 -127
- package/dist/integrations/generators/vault.ts +0 -210
- package/dist/integrations/https.ts +0 -216
- package/dist/integrations/pglite.ts +0 -206
- package/dist/integrations/secret.ts +0 -54
- package/dist/integrations/target.ts +0 -656
- package/dist/integrations/vault.ts +0 -148
- package/dist/integrity/attach.ts +0 -150
- package/dist/integrity/check-threads.ts +0 -166
- package/dist/integrity/constraint.ts +0 -1246
- package/dist/integrity/generators/compiler.ts +0 -138
- package/dist/integrity/generators/gate.ts +0 -227
- package/dist/integrity/generators/issues.ts +0 -160
- package/dist/integrity/generators/publish.ts +0 -232
- package/dist/integrity/generators/trial.ts +0 -359
- package/dist/integrity/lint.ts +0 -214
- package/dist/integrity/lsp/bin.ts +0 -44
- package/dist/integrity/lsp/diagnostics.ts +0 -92
- package/dist/integrity/lsp/findings.ts +0 -63
- package/dist/integrity/lsp/lint.ts +0 -124
- package/dist/integrity/lsp/protocol.ts +0 -78
- package/dist/integrity/lsp/server.ts +0 -224
- package/dist/integrity/slopnator.ts +0 -324
- package/dist/integrity/slopnators/sl_broken_lineage.ts +0 -38
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +0 -60
- package/dist/integrity/slopnators/sl_derived_drift.ts +0 -101
- package/dist/integrity/slopnators/sl_door_in_a_node.ts +0 -250
- package/dist/integrity/slopnators/sl_hardcoded_style.ts +0 -55
- package/dist/integrity/slopnators/sl_inert_island.ts +0 -84
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +0 -93
- package/dist/integrity/slopnators/sl_missing_row.ts +0 -140
- package/dist/integrity/slopnators/sl_no_barrel.ts +0 -48
- package/dist/integrity/slopnators/sl_no_child_process.ts +0 -100
- package/dist/integrity/slopnators/sl_no_comment.ts +0 -43
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +0 -276
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +0 -66
- package/dist/integrity/slopnators/sl_no_such_command.ts +0 -81
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +0 -63
- package/dist/integrity/slopnators/sl_outside_an_operation.ts +0 -25
- package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +0 -70
- package/dist/integrity/slopnators/sl_secret_literal.ts +0 -68
- package/dist/integrity/slopnators/sl_stale_path.ts +0 -178
- package/dist/integrity/slopnators/sl_twice_meant.ts +0 -57
- package/dist/integrity/slopnators/sl_twice_parented.ts +0 -82
- package/dist/integrity/slopnators/sl_undeclared_crossing.ts +0 -481
- package/dist/integrity/slopnators/sl_undeclared_destination.ts +0 -113
- package/dist/integrity/slopnators/sl_undeclared_element.ts +0 -44
- package/dist/integrity/slopnators/sl_undeclared_list.ts +0 -358
- package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -75
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +0 -257
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +0 -152
- package/dist/integrity/slopnators/sl_undeclared_webhook.ts +0 -108
- package/dist/integrity/slopnators/sl_underived_route.ts +0 -72
- package/dist/integrity/slopnators/sl_undescribed.ts +0 -65
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +0 -60
- package/dist/integrity/slopnators/sl_unlisted_program.ts +0 -46
- package/dist/integrity/slopnators/sl_unnamed_island.ts +0 -41
- package/dist/integrity/slopnators/sl_unpaired_member.ts +0 -139
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +0 -71
- package/dist/integrity/slopnators/sl_unprefixed_member.ts +0 -110
- package/dist/integrity/slopnators/sl_unthreaded.ts +0 -421
- package/dist/integrity/slopnators/sl_untranslated_key.ts +0 -140
- package/dist/integrity/slopnators/sl_unvaulted_secret.ts +0 -132
- package/dist/integrity/slopnators/sl_unversioned.ts +0 -52
- package/dist/integrity/sweep.ts +0 -395
- package/dist/knowledge/attach.ts +0 -327
- package/dist/knowledge/generators/docsgen.ts +0 -186
- package/dist/knowledge/generators/glossary.ts +0 -73
- package/dist/knowledge/glossary.ts +0 -6
- package/dist/operations/access.ts +0 -131
- package/dist/operations/attach.ts +0 -408
- package/dist/operations/capabilities.ts +0 -88
- package/dist/operations/dispatch.ts +0 -294
- package/dist/operations/error-results.ts +0 -31
- package/dist/operations/generators/capability.ts +0 -1054
- package/dist/operations/generators/compile.ts +0 -701
- package/dist/operations/generators/permits.ts +0 -224
- package/dist/operations/generators/seed.ts +0 -359
- package/dist/operations/generators/trial.ts +0 -666
- package/dist/operations/nodes.ts +0 -53
- package/dist/operations/operations.ts +0 -91
- package/dist/operations/result.ts +0 -28
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { ANONYMOUS, ENVIRONMENT } from '../core/actor.ts'
|
|
2
|
-
import { HOLDS, type Holds, type Reach } from '../integrations/target.ts'
|
|
3
|
-
import { PERMITS_AT } from './access.ts'
|
|
4
|
-
import { DbConflict } from '../core/errors/catalogue.ts'
|
|
5
|
-
import { beside, quoted, runtime } from './generators/seed.ts'
|
|
6
|
-
import type { ResultCode } from './result.ts'
|
|
7
|
-
|
|
8
|
-
const reaching = (holds: (one: Holds) => boolean): readonly Reach[] =>
|
|
9
|
-
(Object.keys(HOLDS) as Reach[]).filter((one) => holds(HOLDS[one]))
|
|
10
|
-
|
|
11
|
-
const KEEPS = reaching((one) => one.takes === 'entities')
|
|
12
|
-
|
|
13
|
-
const ANSWERS = reaching((one) => one.takes === null)
|
|
14
|
-
|
|
15
|
-
const RUNS = reaching((one) => one.takes === 'calls' && one.addressed === 'nothing')
|
|
16
|
-
|
|
17
|
-
const asked = (reaches: readonly Reach[]): string => reaches.map((one) => "reach === '" + one + "'").join(' || ')
|
|
18
|
-
|
|
19
|
-
export const DISPATCH_AT = 'operations/_dispatch.ts'
|
|
20
|
-
|
|
21
|
-
const REFUSAL: Readonly<Record<ResultCode, string>> = {
|
|
22
|
-
denied: 'not allowed',
|
|
23
|
-
invalid: 'invalid input',
|
|
24
|
-
rate_limited: 'rate limited',
|
|
25
|
-
feature_denied: 'not in plan',
|
|
26
|
-
quota_exceeded: 'quota exceeded',
|
|
27
|
-
error: 'failed',
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const printed = (value: unknown): string => JSON.stringify(value, null, 2)
|
|
31
|
-
|
|
32
|
-
export const dispatched = (
|
|
33
|
-
reached: Readonly<
|
|
34
|
-
Record<
|
|
35
|
-
string,
|
|
36
|
-
{
|
|
37
|
-
readonly reach: string
|
|
38
|
-
readonly address?: string
|
|
39
|
-
readonly headers: Readonly<Record<string, string>>
|
|
40
|
-
readonly sends: readonly { readonly secret: string; readonly header: string; readonly prefix: string }[]
|
|
41
|
-
readonly signs: readonly { readonly secret: string; readonly header: string; readonly prefix: string }[]
|
|
42
|
-
readonly calls: Readonly<Record<string, { readonly method: string | null; readonly path: string }>>
|
|
43
|
-
}
|
|
44
|
-
>
|
|
45
|
-
>,
|
|
46
|
-
): string => [
|
|
47
|
-
"import { createHmac } from 'node:crypto'",
|
|
48
|
-
'',
|
|
49
|
-
`import type { Actor } from ${quoted(runtime(DISPATCH_AT, '../../core/actor.ts'))}`,
|
|
50
|
-
`import type { Allowance, Counting, Operation } from ${quoted(runtime(DISPATCH_AT, '../operations.ts'))}`,
|
|
51
|
-
`import type { FailureIdentity, Result, ResultCode } from ${quoted(runtime(DISPATCH_AT, '../result.ts'))}`,
|
|
52
|
-
'import type { Opened } from ' + quoted(runtime(DISPATCH_AT, '../operations.ts')),
|
|
53
|
-
'import type { Vault } from ' + quoted(runtime(DISPATCH_AT, '../../integrations/secret.ts')),
|
|
54
|
-
`import { permits } from ${quoted(beside(DISPATCH_AT, PERMITS_AT))}`,
|
|
55
|
-
'',
|
|
56
|
-
`const ANONYMOUS: Actor = ${printed(ANONYMOUS)}`,
|
|
57
|
-
'',
|
|
58
|
-
`const ENVIRONMENT: readonly string[] = ${printed(ENVIRONMENT.map(([fact]) => fact))}`,
|
|
59
|
-
'',
|
|
60
|
-
`const REFUSAL: Readonly<Record<ResultCode, string>> = ${printed(REFUSAL)}`,
|
|
61
|
-
'',
|
|
62
|
-
`const CONFLICT = ${printed({ code: DbConflict.def.code, class: DbConflict.def.class })} as FailureIdentity`,
|
|
63
|
-
'',
|
|
64
|
-
'type Reached = Readonly<Record<string, {',
|
|
65
|
-
' readonly reach: string',
|
|
66
|
-
' readonly address?: string',
|
|
67
|
-
' readonly headers: Readonly<Record<string, string>>',
|
|
68
|
-
' readonly sends: readonly { readonly secret: string; readonly header: string; readonly prefix: string }[]',
|
|
69
|
-
' readonly signs: readonly { readonly secret: string; readonly header: string; readonly prefix: string }[]',
|
|
70
|
-
' readonly calls: Readonly<Record<string, { readonly method: string | null; readonly path: string }>>',
|
|
71
|
-
'}>>',
|
|
72
|
-
'',
|
|
73
|
-
'type Reaches = Readonly<Record<string, unknown>>',
|
|
74
|
-
'',
|
|
75
|
-
`const REACHED: Reached = ${printed(reached)}`,
|
|
76
|
-
'',
|
|
77
|
-
"const STOPPED = Symbol.for('thread.operation.stopped')",
|
|
78
|
-
'',
|
|
79
|
-
'const ok = <O>(value: O): Result<O> => ({ ok: true, value })',
|
|
80
|
-
'',
|
|
81
|
-
'const fail = (code: ResultCode, error: string, because?: string, identity?: FailureIdentity): Result<never> => ({',
|
|
82
|
-
' ok: false,',
|
|
83
|
-
' code,',
|
|
84
|
-
' error,',
|
|
85
|
-
' because,',
|
|
86
|
-
' ...(identity === undefined ? {} : { errorCode: identity.code, errorClass: identity.class }),',
|
|
87
|
-
'}) as Result<never>',
|
|
88
|
-
'',
|
|
89
|
-
'const allowance = async (',
|
|
90
|
-
' id: string,',
|
|
91
|
-
' actor: Actor,',
|
|
92
|
-
' counting: Counting | undefined,',
|
|
93
|
-
' held: Allowance | undefined,',
|
|
94
|
-
' code: ResultCode,',
|
|
95
|
-
'): Promise<Result<never> | undefined> => {',
|
|
96
|
-
' if (held === undefined) return undefined',
|
|
97
|
-
' if (counting === undefined) {',
|
|
98
|
-
" return fail('error', REFUSAL['error'], id + ' allows ' + String(held.runs) + ' run(s) per ' + held.per + ' and this dispatch was handed nothing to count with. An operation that states an allowance is handed a counter or it does not run')",
|
|
99
|
-
' }',
|
|
100
|
-
" const seen = await counting['seen'](id + '/' + String(actor.id ?? 'anonymous') + '/' + code, held.per)",
|
|
101
|
-
' if (seen <= held.runs) return undefined',
|
|
102
|
-
" return fail(code, REFUSAL[code], String(seen) + ' of ' + String(held.runs) + ' allowed per ' + held.per)",
|
|
103
|
-
'}',
|
|
104
|
-
'',
|
|
105
|
-
"const strings = (value: unknown): value is string[] => Array.isArray(value) && value.every((one) => typeof one === 'string')",
|
|
106
|
-
'',
|
|
107
|
-
'const person = (value: unknown): Actor | undefined => {',
|
|
108
|
-
" if (typeof value !== 'object' || value === null) return undefined",
|
|
109
|
-
' const one = value as Record<string, unknown>',
|
|
110
|
-
" const id = one['id']",
|
|
111
|
-
" if (!(id === null || typeof id === 'string')) return undefined",
|
|
112
|
-
" const roles = one['roles']",
|
|
113
|
-
" const entitlements = one['entitlements']",
|
|
114
|
-
' if (!strings(roles) || !strings(entitlements)) return undefined',
|
|
115
|
-
' const carried: Record<string, string> = {}',
|
|
116
|
-
' for (const fact of ENVIRONMENT) {',
|
|
117
|
-
' const held = one[fact]',
|
|
118
|
-
' if (held === undefined) continue',
|
|
119
|
-
" if (typeof held !== 'string') return undefined",
|
|
120
|
-
' carried[fact] = held',
|
|
121
|
-
' }',
|
|
122
|
-
' return { ...carried, id, roles, entitlements }',
|
|
123
|
-
'}',
|
|
124
|
-
'',
|
|
125
|
-
'const named = (thrown: unknown, what: string): boolean => {',
|
|
126
|
-
" if (typeof thrown !== 'object' || thrown === null) return false",
|
|
127
|
-
' const one = thrown as { readonly name?: unknown; readonly constructor?: { readonly name?: unknown } }',
|
|
128
|
-
' return one.name === what || one.constructor?.name === what',
|
|
129
|
-
'}',
|
|
130
|
-
'',
|
|
131
|
-
'const stopped = (thrown: unknown): { readonly value: unknown } | undefined => {',
|
|
132
|
-
" if (typeof thrown !== 'object' || thrown === null) return undefined",
|
|
133
|
-
' const one = thrown as Record<PropertyKey, unknown>',
|
|
134
|
-
" return one[STOPPED] === true ? { value: one['value'] } : undefined",
|
|
135
|
-
'}',
|
|
136
|
-
'',
|
|
137
|
-
'const platform = (thrown: unknown): string | undefined => {',
|
|
138
|
-
" if (typeof thrown !== 'object' || thrown === null) return undefined",
|
|
139
|
-
' const said = (thrown as { readonly because?: unknown }).because',
|
|
140
|
-
" return typeof said === 'string' ? said : undefined",
|
|
141
|
-
'}',
|
|
142
|
-
'',
|
|
143
|
-
'const filled = (path: string, fills: Readonly<Record<string, string | number>> | undefined): string => {',
|
|
144
|
-
' let held = path',
|
|
145
|
-
' for (const [name, value] of Object.entries(fills ?? {})) held = held.split(`{${name}}`).join(encodeURIComponent(String(value)))',
|
|
146
|
-
' const left = /\\{([a-z][a-z0-9_]*)\\}/.exec(held)',
|
|
147
|
-
' if (left !== null) throw new Error(`the path ${path} leaves ${left[0]} to be filled and nothing filled it`)',
|
|
148
|
-
' return held',
|
|
149
|
-
'}',
|
|
150
|
-
'',
|
|
151
|
-
'const bound = (word: string, wired: Reached[string], opened: Reaches, opening: Vault | undefined, within: number): Record<string, unknown> | string => {',
|
|
152
|
-
' const reached = opened[word]',
|
|
153
|
-
' const { reach } = wired',
|
|
154
|
-
' if (' + asked(ANSWERS) + ') {',
|
|
155
|
-
' const held = reached',
|
|
156
|
-
' if (held === undefined) {',
|
|
157
|
-
" return word + ' is a disk this operation requires, and this dispatch was handed none under that name. The disk under this process is written in by thread capabilities; one at the other end of a connection is handed in by whoever opened it'",
|
|
158
|
-
' }',
|
|
159
|
-
' return held as unknown as Record<string, unknown>',
|
|
160
|
-
' }',
|
|
161
|
-
' if (' + asked(KEEPS) + ') {',
|
|
162
|
-
' if (reached === undefined) {',
|
|
163
|
-
" return word + ' keeps rows for this app, and this dispatch was handed no store. A store is written in by thread capabilities, out of the target that keeps the entity'",
|
|
164
|
-
' }',
|
|
165
|
-
' return reached as unknown as Record<string, unknown>',
|
|
166
|
-
' }',
|
|
167
|
-
'',
|
|
168
|
-
' const names = Object.keys(wired.calls)',
|
|
169
|
-
' if (names.length === 0) {',
|
|
170
|
-
' return `\\`${word}\\` is required and takes no call, so there is nothing to hand a node. Declare one with \\`thread create target call\\``',
|
|
171
|
-
' }',
|
|
172
|
-
' if (' + asked(RUNS) + ') {',
|
|
173
|
-
' const held = reached',
|
|
174
|
-
' if (held === undefined) {',
|
|
175
|
-
" return word + ' is a program this operation requires, and this dispatch was handed none under that name. A program is written in by thread capabilities, out of the calls declared beside the target'",
|
|
176
|
-
' }',
|
|
177
|
-
' const out: Record<string, unknown> = {}',
|
|
178
|
-
' for (const name of names) {',
|
|
179
|
-
' const doing = (held as unknown as Record<string, unknown>)[name]',
|
|
180
|
-
" if (typeof doing !== 'function') {",
|
|
181
|
-
" return word + ' takes ' + name + ' and the program handed in under that name does not answer to it, so a node would be handed a word that does nothing'",
|
|
182
|
-
' }',
|
|
183
|
-
' out[name] = (args: readonly string[], shows?: boolean, sooner?: number, how?: unknown) => (doing as (a: readonly string[], s?: boolean, w?: number, h?: unknown) => unknown)(args, shows, sooner === undefined ? within : Math.min(sooner, within), how)',
|
|
184
|
-
' }',
|
|
185
|
-
' return out',
|
|
186
|
-
' }',
|
|
187
|
-
' if (reached === undefined) {',
|
|
188
|
-
" return word + ' is reached over ' + wired.reach + ', and this dispatch was handed nothing to send an outward call through. thread capabilities writes that in; or take that word off the row that requires it'",
|
|
189
|
-
' }',
|
|
190
|
-
' const wanted = [...wired.sends.map((one) => one.secret), ...wired.signs.map((one) => one.secret)]',
|
|
191
|
-
' if (wanted.length > 0 && opening === undefined) {',
|
|
192
|
-
" return word + ' needs ' + wanted.join(', ') + ' and this dispatch was handed no vault to open them. A target is reached with what opens it or not at all'",
|
|
193
|
-
' }',
|
|
194
|
-
' const out: Record<string, unknown> = {}',
|
|
195
|
-
' for (const name of names) {',
|
|
196
|
-
' const call = wired.calls[name]',
|
|
197
|
-
' if (call === undefined) continue',
|
|
198
|
-
' out[name] = async (given?: { fills?: Readonly<Record<string, string | number>>; json?: unknown; body?: Uint8Array; contentType?: string; told?: (said: unknown) => void }) => {',
|
|
199
|
-
" const values = wanted.length === 0 ? {} : await opening",
|
|
200
|
-
' const headers: Record<string, string> = {}',
|
|
201
|
-
' for (const [name, value] of Object.entries(wired.headers)) headers[name] = value',
|
|
202
|
-
' for (const one of wired.sends) headers[one.header] = `${one.prefix}${values[one.secret] ?? \'\'}`',
|
|
203
|
-
' const body = given?.json === undefined ? given?.body : new TextEncoder().encode(JSON.stringify(given.json))',
|
|
204
|
-
' for (const one of wired.signs) {',
|
|
205
|
-
" const mac = createHmac('sha256', values[one.secret] ?? '').update(body ?? new Uint8Array()).digest('hex')",
|
|
206
|
-
' headers[one.header] = one.prefix + mac',
|
|
207
|
-
' }',
|
|
208
|
-
" const send = reached as { call: (request: unknown, within?: number) => Promise<unknown> }",
|
|
209
|
-
" return await send['call']({",
|
|
210
|
-
' target: word,',
|
|
211
|
-
' address: wired.address ?? \'\',',
|
|
212
|
-
" method: call.method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',",
|
|
213
|
-
' path: filled(call.path, given?.fills),',
|
|
214
|
-
' headers,',
|
|
215
|
-
' ...(body === undefined ? {} : { body, contentType: given?.contentType ?? \'application/json\' }),',
|
|
216
|
-
' ...(given?.told === undefined ? {} : { told: given.told }),',
|
|
217
|
-
' }, within)',
|
|
218
|
-
' }',
|
|
219
|
-
' }',
|
|
220
|
-
' return out',
|
|
221
|
-
'}',
|
|
222
|
-
'',
|
|
223
|
-
'const given = (operation: Operation<unknown, string>, actor: Actor, opened: Reaches, opening: Vault | undefined, telling: (said: unknown) => void): Record<string, unknown> | string => {',
|
|
224
|
-
' const held: Record<string, unknown> = { actor, tell: telling }',
|
|
225
|
-
' for (const word of operation.requires) {',
|
|
226
|
-
' const wired = REACHED[word]',
|
|
227
|
-
' if (wired === undefined) {',
|
|
228
|
-
' return `\\`${operation[\'id\']}\\` requires \\`${word}\\`, and this app declares no target by that name. An operation requires targets: declare one with \\`thread create target\\`, or take that word off its row`',
|
|
229
|
-
' }',
|
|
230
|
-
' const reached = bound(word, wired, opened, opening, operation.timeout)',
|
|
231
|
-
" if (typeof reached === 'string') return reached",
|
|
232
|
-
' held[word] = reached',
|
|
233
|
-
' }',
|
|
234
|
-
' return held',
|
|
235
|
-
'}',
|
|
236
|
-
'',
|
|
237
|
-
'export async function dispatch<I, R extends string = string>(',
|
|
238
|
-
' operation: Operation<I, R>,',
|
|
239
|
-
' rawInput: unknown,',
|
|
240
|
-
' asking: Actor,',
|
|
241
|
-
' opened: Reaches,',
|
|
242
|
-
' opening: Vault | undefined,',
|
|
243
|
-
' counting?: Counting,',
|
|
244
|
-
' telling?: (said: unknown) => void,',
|
|
245
|
-
'): Promise<Result<unknown>> {',
|
|
246
|
-
' const id = operation.id',
|
|
247
|
-
' const who = person(asking)',
|
|
248
|
-
' const actor: Actor = who ?? ANONYMOUS',
|
|
249
|
-
' try {',
|
|
250
|
-
" if (who === undefined) return fail('denied', REFUSAL['denied'], 'identity_invalid')",
|
|
251
|
-
' const parsed = operation.input.safeParse(rawInput)',
|
|
252
|
-
' if (!parsed.success) {',
|
|
253
|
-
" return fail('invalid', REFUSAL['invalid'], String(parsed['error']))",
|
|
254
|
-
' }',
|
|
255
|
-
' const { resource } = operation',
|
|
256
|
-
' const ruled = permits(actor, id, resource(parsed[\'data\']))',
|
|
257
|
-
' const { allow, because: why } = ruled',
|
|
258
|
-
" if (!allow) return fail('denied', REFUSAL['denied'], why)",
|
|
259
|
-
'',
|
|
260
|
-
' const { entitled } = operation',
|
|
261
|
-
' if (entitled !== undefined && !actor.entitlements.includes(entitled)) {',
|
|
262
|
-
" return fail('feature_denied', REFUSAL['feature_denied'], id + ' is offered on ' + entitled + ', which this caller does not hold')",
|
|
263
|
-
' }',
|
|
264
|
-
'',
|
|
265
|
-
" const limited = await allowance(id, actor, counting, operation.rate, 'rate_limited')",
|
|
266
|
-
' if (limited !== undefined) return limited',
|
|
267
|
-
" const over = await allowance(id, actor, counting, operation.quota, 'quota_exceeded')",
|
|
268
|
-
' if (over !== undefined) return over',
|
|
269
|
-
'',
|
|
270
|
-
' const handed = given(operation as Operation<unknown, string>, actor, opened, opening, telling ?? (() => undefined))',
|
|
271
|
-
" if (typeof handed === 'string') return fail('error', REFUSAL['error'], handed)",
|
|
272
|
-
'',
|
|
273
|
-
' const held: { readonly input: I } & Record<string, unknown> = { input: parsed.data }',
|
|
274
|
-
' const value = await operation.run(handed as Opened<R>, held)',
|
|
275
|
-
' return ok(value)',
|
|
276
|
-
' } catch (thrown) {',
|
|
277
|
-
' const early = stopped(thrown)',
|
|
278
|
-
' if (early !== undefined) {',
|
|
279
|
-
' return ok(early[\'value\'])',
|
|
280
|
-
' }',
|
|
281
|
-
'',
|
|
282
|
-
' const message = thrown instanceof Error ? thrown.message : String(thrown)',
|
|
283
|
-
'',
|
|
284
|
-
" if (named(thrown, 'VersionConflictError')) {",
|
|
285
|
-
" return fail('invalid', message, 'version_conflict', CONFLICT)",
|
|
286
|
-
' }',
|
|
287
|
-
'',
|
|
288
|
-
" const code: ResultCode = named(thrown, 'ValidationError') ? 'invalid' : 'error'",
|
|
289
|
-
' const because = platform(thrown) ?? message',
|
|
290
|
-
' return fail(code, message, because)',
|
|
291
|
-
' }',
|
|
292
|
-
'}',
|
|
293
|
-
'',
|
|
294
|
-
].join('\n')
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { PlatformError, catalog, type ErrorDef } from '../core/errors/error.ts'
|
|
2
|
-
import { AuthDenied, FeatureNotInPlan, InternalInvalidArgument, InternalUnexpected, QuotaExceeded, RateLimitExceeded } from '../core/errors/catalogue.ts'
|
|
3
|
-
|
|
4
|
-
import type { Result, ResultCode } from './result.ts'
|
|
5
|
-
|
|
6
|
-
const RESULT_ERROR_DEFS: Readonly<Record<ResultCode, ErrorDef>> = {
|
|
7
|
-
invalid: InternalInvalidArgument.def,
|
|
8
|
-
denied: AuthDenied.def,
|
|
9
|
-
rate_limited: RateLimitExceeded.def,
|
|
10
|
-
quota_exceeded: QuotaExceeded.def,
|
|
11
|
-
feature_denied: FeatureNotInPlan.def,
|
|
12
|
-
error: InternalUnexpected.def,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function errorDefForResult(code: ResultCode): ErrorDef {
|
|
16
|
-
return RESULT_ERROR_DEFS[code]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function platformErrorForResult(result: Result<unknown>, run: string): PlatformError {
|
|
20
|
-
if (result.ok) throw new TypeError('cannot render a successful Result as a problem')
|
|
21
|
-
|
|
22
|
-
const def = result.errorCode === undefined ? errorDefForResult(result.code) : catalog.get(result.errorCode)
|
|
23
|
-
const resolved = def ?? InternalUnexpected.def
|
|
24
|
-
const detail = result.errorCode === undefined && resolved.code === InternalUnexpected.def.code ? undefined : result.error
|
|
25
|
-
|
|
26
|
-
return new PlatformError(resolved, {
|
|
27
|
-
...(detail === undefined ? {} : { detail }),
|
|
28
|
-
...(result.because === undefined ? {} : { because: result.because }),
|
|
29
|
-
traceId: run,
|
|
30
|
-
})
|
|
31
|
-
}
|