@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,191 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process'
|
|
2
|
-
import { argv, stderr, stdout } from 'node:process'
|
|
3
|
-
|
|
4
|
-
import type { Env } from '../core/actor.ts'
|
|
5
|
-
import { answer, asking, given, unasked, wordsIn } from '../core/answers.ts'
|
|
6
|
-
import { readFileSync } from '../core/files.ts'
|
|
7
|
-
import { exit } from '../core/invoked.ts'
|
|
8
|
-
import { valuesIn } from '../core/rows.ts'
|
|
9
|
-
import { findTree, inTree } from '../core/tree.ts'
|
|
10
|
-
|
|
11
|
-
import { REACH, reachOf, type Target } from './target.ts'
|
|
12
|
-
import { opened } from './vault.ts'
|
|
13
|
-
|
|
14
|
-
const PRODUCTION = '--production'
|
|
15
|
-
|
|
16
|
-
const METHOD = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/
|
|
17
|
-
|
|
18
|
-
function isClass(fn: (...args: readonly unknown[]) => unknown): boolean {
|
|
19
|
-
return /^class[\s{]/.test(Function.prototype.toString.call(fn))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const ASKS: readonly { readonly key: string; readonly says: string }[] = [
|
|
23
|
-
{
|
|
24
|
-
key: 'args',
|
|
25
|
-
says: 'a JSON value — an array for positional arguments, anything else for one, blank for none',
|
|
26
|
-
},
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
const TAKES: readonly string[] = ASKS.map((one) => one.key)
|
|
30
|
-
|
|
31
|
-
const ASKED: readonly string[] = ASKS.map((one) => `\`${one.key}\`: ${one.says}`)
|
|
32
|
-
|
|
33
|
-
export async function ran(name: string): Promise<void> {
|
|
34
|
-
const say = (because: string): never => {
|
|
35
|
-
stderr.write(`${name}: ${because}\n`)
|
|
36
|
-
exit(1)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const tree = findTree()
|
|
40
|
-
let targets: readonly Target[] = []
|
|
41
|
-
try {
|
|
42
|
-
const taken = valuesIn(readFileSync(inTree(tree, 'targets.ts'))) as {
|
|
43
|
-
TARGETS?: readonly Target[]
|
|
44
|
-
}
|
|
45
|
-
targets = taken.TARGETS ?? []
|
|
46
|
-
} catch {
|
|
47
|
-
return say('`_threads/targets.ts` could not be read, so nothing says what this integration is.')
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const mine = targets.find((one) => one.id === `${REACH.cli}${name}` || one.id === `${REACH.module}${name}`)
|
|
51
|
-
if (mine === undefined) {
|
|
52
|
-
return say(
|
|
53
|
-
`no target called \`${REACH.cli}${name}\` or \`${REACH.module}${name}\` is declared here, so this command should ` +
|
|
54
|
-
'not exist. Declare it with `thread create target`, or take its row out of `_threads/derivation.ts`.',
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const does = mine.offers ?? []
|
|
59
|
-
const passed = argv.slice(2)
|
|
60
|
-
const sub = passed[0]
|
|
61
|
-
|
|
62
|
-
if (sub === undefined) {
|
|
63
|
-
stdout.write(
|
|
64
|
-
`${name}: ${mine.describes}\n\n${String(does.length)} command(s), read off \`${mine.uses ?? ''}\` when \`${mine.id}\` was declared:\n` +
|
|
65
|
-
`${does.join(', ') || '(none were readable — name them with `thread create target edit`)'}\n\n` +
|
|
66
|
-
`thread run ${name} <command> [anything else] — arguments and flags reach \`${mine.uses ?? ''}\` untouched.\n` +
|
|
67
|
-
`It takes ${String(ASKED.length)} answer(s): ${ASKED.join('; ')}.\n`,
|
|
68
|
-
)
|
|
69
|
-
exit(0)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!does.includes(sub)) {
|
|
73
|
-
return say(
|
|
74
|
-
`\`${sub}\` is not one of the ${String(does.length)} command(s) \`${mine.uses ?? ''}\` stated when \`${mine.id}\` ` +
|
|
75
|
-
`was declared. It declared: ${does.join(', ') || '(nothing)'}. If the tool has changed, read it again with ` +
|
|
76
|
-
`\`thread create target edit ${mine.id}\`.`,
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (reachOf(mine.id) === 'module') {
|
|
81
|
-
return run({ name, module: mine.uses ?? '', requires: mine.requires })
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const went = spawnSync(mine.uses ?? '', passed, { cwd: tree.at, stdio: 'inherit' })
|
|
85
|
-
exit(went.status ?? 1)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function run(declared: {
|
|
89
|
-
readonly name: string
|
|
90
|
-
readonly module: string
|
|
91
|
-
readonly requires: readonly string[]
|
|
92
|
-
}): Promise<void> {
|
|
93
|
-
const refuse = (because: string): never => {
|
|
94
|
-
stderr.write(`${declared.name}: ${because}\n`)
|
|
95
|
-
exit(1)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const method = wordsIn([])[0]
|
|
99
|
-
if (method === undefined) {
|
|
100
|
-
return refuse(
|
|
101
|
-
`takes the method to call, dot-separated for a namespaced one — e.g. \`thread run ${declared.name} customers.list\`.`,
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
if (!METHOD.test(method)) {
|
|
105
|
-
return refuse(`\`${method}\` is not a method path — one name, or names joined by dots, e.g. \`customers.list\``)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const piped = given()
|
|
109
|
-
unasked(piped, TAKES, `\`thread run ${declared.name} ${method}\``)
|
|
110
|
-
const { ask, shut } = asking(piped)
|
|
111
|
-
const typed = (
|
|
112
|
-
piped.length > 0
|
|
113
|
-
? (answer(piped, TAKES[0] ?? 'args') ?? '')
|
|
114
|
-
: await ask('args', 'args, as JSON — an array for positional arguments, anything else for one (blank for none): ')
|
|
115
|
-
).trim()
|
|
116
|
-
shut()
|
|
117
|
-
|
|
118
|
-
let args: unknown[] = []
|
|
119
|
-
if (typed !== '') {
|
|
120
|
-
let parsed: unknown
|
|
121
|
-
try {
|
|
122
|
-
parsed = JSON.parse(typed)
|
|
123
|
-
} catch (thrown) {
|
|
124
|
-
return refuse(`that is not JSON: ${thrown instanceof Error ? thrown.message : String(thrown)}. Nothing was called.`)
|
|
125
|
-
}
|
|
126
|
-
args = Array.isArray(parsed) ? parsed : [parsed]
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const env: Env = argv.includes(PRODUCTION) ? 'production' : 'sandbox'
|
|
130
|
-
|
|
131
|
-
const opening = opened(findTree().at, declared.requires, env)
|
|
132
|
-
let resolved: Readonly<Record<string, string>>
|
|
133
|
-
try {
|
|
134
|
-
resolved = await opening(declared.requires)
|
|
135
|
-
} catch (thrown) {
|
|
136
|
-
return refuse(thrown instanceof Error ? thrown.message : String(thrown))
|
|
137
|
-
}
|
|
138
|
-
const credentials = declared.requires.map((one) => resolved[one] ?? '')
|
|
139
|
-
|
|
140
|
-
let mod: Record<string, unknown>
|
|
141
|
-
try {
|
|
142
|
-
mod = (await import(declared.module)) as Record<string, unknown>
|
|
143
|
-
} catch (thrown) {
|
|
144
|
-
return refuse(
|
|
145
|
-
`\`${declared.module}\` could not be imported: ${thrown instanceof Error ? thrown.message : String(thrown)}. It was ` +
|
|
146
|
-
`installed when this was declared — if it is gone, \`npm install ${declared.module}\` puts it back.`,
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const entry: unknown = mod.default ?? mod
|
|
151
|
-
|
|
152
|
-
const asFunction = entry as (...a: readonly unknown[]) => unknown
|
|
153
|
-
const asConstructor = entry as new (...a: readonly unknown[]) => unknown
|
|
154
|
-
|
|
155
|
-
const REACHES = new Set(['constructor', '__proto__', 'prototype'])
|
|
156
|
-
|
|
157
|
-
const client: unknown =
|
|
158
|
-
typeof entry !== 'function' ? entry : isClass(asFunction) ? Reflect.construct(asConstructor, credentials) : asFunction(...credentials)
|
|
159
|
-
|
|
160
|
-
let owner: unknown = client
|
|
161
|
-
let target: unknown = client
|
|
162
|
-
for (const segment of method.split('.')) {
|
|
163
|
-
if (REACHES.has(segment)) {
|
|
164
|
-
return refuse(
|
|
165
|
-
`\`${segment}\` is not an operation of \`${declared.module}\` — it is a step every JavaScript value has, and ` +
|
|
166
|
-
'walking to it leaves what was installed. What may run here is what the module exports, and a person fixed that ' +
|
|
167
|
-
'set when they declared it.',
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
owner = target
|
|
171
|
-
target = (target as Record<string, unknown> | null | undefined)?.[segment]
|
|
172
|
-
}
|
|
173
|
-
if (typeof target !== 'function') {
|
|
174
|
-
return refuse(`\`${method}\` on \`${declared.module}\` is ${target === undefined ? 'nothing' : `a ${typeof target}`}, not a method that can be called`)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let result: unknown
|
|
178
|
-
try {
|
|
179
|
-
result = await (target as (...a: unknown[]) => unknown).apply(owner, args)
|
|
180
|
-
} catch (thrown) {
|
|
181
|
-
return refuse(`\`${method}\` on \`${declared.module}\` threw: ${thrown instanceof Error ? thrown.message : String(thrown)}`)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
stdout.write(`${declared.name}: ${method} (${env})\n`)
|
|
185
|
-
try {
|
|
186
|
-
stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
187
|
-
} catch {
|
|
188
|
-
stdout.write(`${String(result)}\n`)
|
|
189
|
-
}
|
|
190
|
-
exit(0)
|
|
191
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from "../../core/files.js";
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
const STORES = 'src/stores';
|
|
4
|
-
/* What hands an operation its seams, seeded once and then the app's own. */
|
|
5
|
-
const SEAMS = 'src/seams.ts';
|
|
6
|
-
/* What the declarations settle, which the seams file is expected to hand on. */
|
|
7
|
-
const DECLARED = 'DECLARED';
|
|
8
|
-
/*
|
|
9
|
-
* WHAT THIS READS, AND WHY IT READS THAT RATHER THAN THE TARGETS SHEET.
|
|
10
|
-
*
|
|
11
|
-
* A target that keeps entities derives a store of its own, written by `thread schema` into
|
|
12
|
-
* `src/stores/<target>.ts`. So one of those files existing IS the fact that this app declares
|
|
13
|
-
* somewhere to keep rows — already resolved, by the command that resolved it, with no second reading
|
|
14
|
-
* of what a reach can hold and no chance of the two disagreeing about it.
|
|
15
|
-
*
|
|
16
|
-
* Where rows go is settled in `src/_generated/seams.ts`, which `thread capabilities` writes from those
|
|
17
|
-
* stores and refuses when it drifts. The one place it can still go wrong is `src/seams.ts`, which is
|
|
18
|
-
* the app's own: an app that stops handing on what was settled there hands its operations a store
|
|
19
|
-
* that keeps nothing, while the tables are written, the migrations are written, and the gate is green.
|
|
20
|
-
*/
|
|
21
|
-
function held(root, at) {
|
|
22
|
-
const path = resolve(root, at);
|
|
23
|
-
try {
|
|
24
|
-
return existsSync(path) ? readFileSync(path, 'utf8') : '';
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return '';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function derived(root) {
|
|
31
|
-
const at = resolve(root, STORES);
|
|
32
|
-
try {
|
|
33
|
-
return existsSync(at) ? readdirSync(at).filter((one) => one.endsWith('.ts')) : [];
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export const slopnator = {
|
|
40
|
-
checks: (root) => {
|
|
41
|
-
const stores = derived(root);
|
|
42
|
-
if (stores.length === 0)
|
|
43
|
-
return [];
|
|
44
|
-
/*
|
|
45
|
-
* AN APP WITH NOTHING THAT HANDS A STORE TO AN OPERATION IS NOT KEEPING ROWS THROUGH ONE, SO
|
|
46
|
-
* THERE IS NOTHING TO REFUSE.
|
|
47
|
-
*
|
|
48
|
-
* The seams file is seeded by `thread capabilities`, which ships in the operations package — so
|
|
49
|
-
* an app that attached data and integrity and nothing else has no seams and no command that
|
|
50
|
-
* seeds one. Its absence means rows go nowhere yet, which is a state to pass rather than refuse:
|
|
51
|
-
* nothing is being lost, because nothing is being kept.
|
|
52
|
-
*/
|
|
53
|
-
const where = held(root, SEAMS);
|
|
54
|
-
if (where === '')
|
|
55
|
-
return [];
|
|
56
|
-
if (where.includes(DECLARED))
|
|
57
|
-
return [];
|
|
58
|
-
return [
|
|
59
|
-
`${STORES} holds ${stores.join(', ')} and ${SEAMS} no longer hands on ${DECLARED}, so every row this app keeps goes ` +
|
|
60
|
-
'somewhere that is gone the moment it stops. The tables are written, the migrations are written, the gate is ' +
|
|
61
|
-
`green, and the database is never opened. Spread ${DECLARED} back into SEAMS, or name its store there`,
|
|
62
|
-
];
|
|
63
|
-
},
|
|
64
|
-
proves: {
|
|
65
|
-
wrong: [
|
|
66
|
-
{
|
|
67
|
-
files: [
|
|
68
|
-
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
69
|
-
{
|
|
70
|
-
at: 'src/seams.ts',
|
|
71
|
-
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
at: 'src/seams.ts',
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
right: [
|
|
78
|
-
{
|
|
79
|
-
files: [
|
|
80
|
-
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
81
|
-
{
|
|
82
|
-
at: 'src/seams.ts',
|
|
83
|
-
source: "import { DECLARED } from './_generated/seams.ts'\n\nexport const SEAMS = { ...DECLARED }\n",
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
at: 'src/seams.ts',
|
|
87
|
-
},
|
|
88
|
-
/* An app that derived a store and has nothing handing one to an operation — data attached, operations not. */
|
|
89
|
-
{
|
|
90
|
-
files: [{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' }],
|
|
91
|
-
at: 'src/seams.ts',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
files: [
|
|
95
|
-
{
|
|
96
|
-
at: 'src/seams.ts',
|
|
97
|
-
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
at: 'src/seams.ts',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
},
|
|
104
|
-
};
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from '../../core/files.ts'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
|
|
4
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
5
|
-
|
|
6
|
-
const STORES = 'src/stores'
|
|
7
|
-
|
|
8
|
-
/* What hands an operation its seams, seeded once and then the app's own. */
|
|
9
|
-
const SEAMS = 'src/seams.ts'
|
|
10
|
-
|
|
11
|
-
/* What the declarations settle, which the seams file is expected to hand on. */
|
|
12
|
-
const DECLARED = 'DECLARED'
|
|
13
|
-
|
|
14
|
-
/*
|
|
15
|
-
* WHAT THIS READS, AND WHY IT READS THAT RATHER THAN THE TARGETS SHEET.
|
|
16
|
-
*
|
|
17
|
-
* A target that keeps entities derives a store of its own, written by `thread schema` into
|
|
18
|
-
* `src/stores/<target>.ts`. So one of those files existing IS the fact that this app declares
|
|
19
|
-
* somewhere to keep rows — already resolved, by the command that resolved it, with no second reading
|
|
20
|
-
* of what a reach can hold and no chance of the two disagreeing about it.
|
|
21
|
-
*
|
|
22
|
-
* Where rows go is settled in `src/_generated/seams.ts`, which `thread capabilities` writes from those
|
|
23
|
-
* stores and refuses when it drifts. The one place it can still go wrong is `src/seams.ts`, which is
|
|
24
|
-
* the app's own: an app that stops handing on what was settled there hands its operations a store
|
|
25
|
-
* that keeps nothing, while the tables are written, the migrations are written, and the gate is green.
|
|
26
|
-
*/
|
|
27
|
-
function held(root: string, at: string): string {
|
|
28
|
-
const path = resolve(root, at)
|
|
29
|
-
try {
|
|
30
|
-
return existsSync(path) ? readFileSync(path, 'utf8') : ''
|
|
31
|
-
} catch {
|
|
32
|
-
return ''
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function derived(root: string): readonly string[] {
|
|
37
|
-
const at = resolve(root, STORES)
|
|
38
|
-
try {
|
|
39
|
-
return existsSync(at) ? readdirSync(at).filter((one) => one.endsWith('.ts')) : []
|
|
40
|
-
} catch {
|
|
41
|
-
return []
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const slopnator: Slopnator = {
|
|
46
|
-
checks: (root) => {
|
|
47
|
-
const stores = derived(root)
|
|
48
|
-
if (stores.length === 0) return []
|
|
49
|
-
|
|
50
|
-
/*
|
|
51
|
-
* AN APP WITH NOTHING THAT HANDS A STORE TO AN OPERATION IS NOT KEEPING ROWS THROUGH ONE, SO
|
|
52
|
-
* THERE IS NOTHING TO REFUSE.
|
|
53
|
-
*
|
|
54
|
-
* The seams file is seeded by `thread capabilities`, which ships in the operations package — so
|
|
55
|
-
* an app that attached data and integrity and nothing else has no seams and no command that
|
|
56
|
-
* seeds one. Its absence means rows go nowhere yet, which is a state to pass rather than refuse:
|
|
57
|
-
* nothing is being lost, because nothing is being kept.
|
|
58
|
-
*/
|
|
59
|
-
const where = held(root, SEAMS)
|
|
60
|
-
if (where === '') return []
|
|
61
|
-
|
|
62
|
-
if (where.includes(DECLARED)) return []
|
|
63
|
-
|
|
64
|
-
return [
|
|
65
|
-
`${STORES} holds ${stores.join(', ')} and ${SEAMS} no longer hands on ${DECLARED}, so every row this app keeps goes ` +
|
|
66
|
-
'somewhere that is gone the moment it stops. The tables are written, the migrations are written, the gate is ' +
|
|
67
|
-
`green, and the database is never opened. Spread ${DECLARED} back into SEAMS, or name its store there`,
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
|
-
proves: {
|
|
71
|
-
wrong: [
|
|
72
|
-
{
|
|
73
|
-
files: [
|
|
74
|
-
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
75
|
-
{
|
|
76
|
-
at: 'src/seams.ts',
|
|
77
|
-
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
at: 'src/seams.ts',
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
right: [
|
|
84
|
-
{
|
|
85
|
-
files: [
|
|
86
|
-
{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' },
|
|
87
|
-
{
|
|
88
|
-
at: 'src/seams.ts',
|
|
89
|
-
source: "import { DECLARED } from './_generated/seams.ts'\n\nexport const SEAMS = { ...DECLARED }\n",
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
at: 'src/seams.ts',
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
/* An app that derived a store and has nothing handing one to an operation — data attached, operations not. */
|
|
96
|
-
{
|
|
97
|
-
files: [{ at: 'src/stores/pl_probe.ts', source: 'export const store = {}\n' }],
|
|
98
|
-
at: 'src/seams.ts',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
files: [
|
|
102
|
-
{
|
|
103
|
-
at: 'src/seams.ts',
|
|
104
|
-
source: "import { inMemorySeams } from '../../operations/memory.ts'\n\nexport const SEAMS = inMemorySeams().seams\n",
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
at: 'src/seams.ts',
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
},
|
|
111
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { isBuiltin } from 'node:module';
|
|
2
|
-
import { parser } from 'typescript-eslint';
|
|
3
|
-
const CODE = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
4
|
-
const MENTIONS = /\bimport\b|\brequire\s*\(/;
|
|
5
|
-
function reached(node, seen, found) {
|
|
6
|
-
if (typeof node !== 'object' || node === null)
|
|
7
|
-
return;
|
|
8
|
-
if (seen.has(node))
|
|
9
|
-
return;
|
|
10
|
-
seen.add(node);
|
|
11
|
-
if (Array.isArray(node)) {
|
|
12
|
-
for (const each of node)
|
|
13
|
-
reached(each, seen, found);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const one = node;
|
|
17
|
-
const type = one['type'];
|
|
18
|
-
if (type === 'ImportDeclaration' || type === 'ImportExpression' || type === 'ExportNamedDeclaration' || type === 'ExportAllDeclaration') {
|
|
19
|
-
const source = one['source'];
|
|
20
|
-
if (typeof source?.value === 'string')
|
|
21
|
-
found(source.value);
|
|
22
|
-
}
|
|
23
|
-
if (type === 'CallExpression') {
|
|
24
|
-
const callee = one['callee'];
|
|
25
|
-
if (callee?.type === 'Identifier' && callee.name === 'require') {
|
|
26
|
-
const first = one['arguments']?.[0];
|
|
27
|
-
if (typeof first?.value === 'string')
|
|
28
|
-
found(first.value);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
for (const held of Object.values(one))
|
|
32
|
-
reached(held, seen, found);
|
|
33
|
-
}
|
|
34
|
-
export const slopnator = {
|
|
35
|
-
holds: (_node, at) => {
|
|
36
|
-
if (!CODE.test(at.filename))
|
|
37
|
-
return false;
|
|
38
|
-
const text = String(at.text);
|
|
39
|
-
if (!MENTIONS.test(text))
|
|
40
|
-
return false;
|
|
41
|
-
let ast;
|
|
42
|
-
try {
|
|
43
|
-
ast = parser.parseForESLint(text, {
|
|
44
|
-
range: true,
|
|
45
|
-
ecmaVersion: 'latest',
|
|
46
|
-
sourceType: 'module',
|
|
47
|
-
ecmaFeatures: { jsx: true },
|
|
48
|
-
}).ast;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
const taken = new Set();
|
|
54
|
-
reached(ast, new WeakSet(), (one) => {
|
|
55
|
-
if (isBuiltin(one))
|
|
56
|
-
taken.add(one);
|
|
57
|
-
});
|
|
58
|
-
if (taken.size === 0)
|
|
59
|
-
return false;
|
|
60
|
-
return `this reaches ${[...taken].sort().join(', ')} straight from the runtime, and a node is handed everything it may touch`;
|
|
61
|
-
},
|
|
62
|
-
proves: {
|
|
63
|
-
wrong: [
|
|
64
|
-
"import { readFileSync } from 'node:fs'\nexport const node = readFileSync\n",
|
|
65
|
-
"const { spawn } = require('child_process')\nexport const node = spawn\n",
|
|
66
|
-
"export const node = async () => await import('node:net')\n",
|
|
67
|
-
],
|
|
68
|
-
right: [
|
|
69
|
-
"import { nd_read } from '../../operations/nodes.ts'\nexport const node = nd_read(async (hands) => await hands.store.get('a', 'b'))\n",
|
|
70
|
-
"import type { In } from './input.ts'\nexport const node = null as unknown as In\n",
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { isBuiltin } from 'node:module'
|
|
2
|
-
|
|
3
|
-
import { parser } from 'typescript-eslint'
|
|
4
|
-
|
|
5
|
-
import type { Slopnator } from '../slopnator.ts'
|
|
6
|
-
|
|
7
|
-
const CODE = /\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/
|
|
8
|
-
|
|
9
|
-
const MENTIONS = /\bimport\b|\brequire\s*\(/
|
|
10
|
-
|
|
11
|
-
function reached(node: unknown, seen: WeakSet<object>, found: (one: string) => void): void {
|
|
12
|
-
if (typeof node !== 'object' || node === null) return
|
|
13
|
-
if (seen.has(node)) return
|
|
14
|
-
seen.add(node)
|
|
15
|
-
|
|
16
|
-
if (Array.isArray(node)) {
|
|
17
|
-
for (const each of node) reached(each, seen, found)
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const one = node as Record<string, unknown>
|
|
22
|
-
const type = one['type']
|
|
23
|
-
|
|
24
|
-
if (type === 'ImportDeclaration' || type === 'ImportExpression' || type === 'ExportNamedDeclaration' || type === 'ExportAllDeclaration') {
|
|
25
|
-
const source = one['source'] as { readonly value?: unknown } | null | undefined
|
|
26
|
-
if (typeof source?.value === 'string') found(source.value)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (type === 'CallExpression') {
|
|
30
|
-
const callee = one['callee'] as { readonly type?: unknown; readonly name?: unknown } | null | undefined
|
|
31
|
-
if (callee?.type === 'Identifier' && callee.name === 'require') {
|
|
32
|
-
const first = (one['arguments'] as readonly { readonly value?: unknown }[] | undefined)?.[0]
|
|
33
|
-
if (typeof first?.value === 'string') found(first.value)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const held of Object.values(one)) reached(held, seen, found)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const slopnator: Slopnator = {
|
|
41
|
-
holds: (_node, at) => {
|
|
42
|
-
if (!CODE.test(at.filename)) return false
|
|
43
|
-
|
|
44
|
-
const text = String(at.text)
|
|
45
|
-
if (!MENTIONS.test(text)) return false
|
|
46
|
-
|
|
47
|
-
let ast: unknown
|
|
48
|
-
try {
|
|
49
|
-
ast = (parser.parseForESLint as (text: string, options: unknown) => { readonly ast: unknown })(text, {
|
|
50
|
-
range: true,
|
|
51
|
-
ecmaVersion: 'latest',
|
|
52
|
-
sourceType: 'module',
|
|
53
|
-
ecmaFeatures: { jsx: true },
|
|
54
|
-
}).ast
|
|
55
|
-
} catch {
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const taken = new Set<string>()
|
|
60
|
-
reached(ast, new WeakSet<object>(), (one) => {
|
|
61
|
-
if (isBuiltin(one)) taken.add(one)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
if (taken.size === 0) return false
|
|
65
|
-
|
|
66
|
-
return `this reaches ${[...taken].sort().join(', ')} straight from the runtime, and a node is handed everything it may touch`
|
|
67
|
-
},
|
|
68
|
-
proves: {
|
|
69
|
-
wrong: [
|
|
70
|
-
"import { readFileSync } from 'node:fs'\nexport const node = readFileSync\n",
|
|
71
|
-
"const { spawn } = require('child_process')\nexport const node = spawn\n",
|
|
72
|
-
"export const node = async () => await import('node:net')\n",
|
|
73
|
-
],
|
|
74
|
-
right: [
|
|
75
|
-
"import { nd_read } from '../../operations/nodes.ts'\nexport const node = nd_read(async (hands) => await hands.store.get('a', 'b'))\n",
|
|
76
|
-
"import type { In } from './input.ts'\nexport const node = null as unknown as In\n",
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface CostAmount {
|
|
2
|
-
readonly usdMicroCents: number;
|
|
3
|
-
readonly chargedMicroCents: number;
|
|
4
|
-
readonly currency: string;
|
|
5
|
-
readonly meter: string;
|
|
6
|
-
}
|
|
7
|
-
export declare class InvalidCostError extends Error {
|
|
8
|
-
constructor(message: string);
|
|
9
|
-
}
|
|
10
|
-
export declare function assertValidCost(cost: CostAmount): void;
|
|
11
|
-
export declare const MICRO_CENTS_PER_CENT = 1000000;
|
|
12
|
-
export declare const MICRO_CENTS_PER_UNIT = 100000000;
|
|
13
|
-
export declare function toMicroCents(amount: number): number;
|
|
14
|
-
export declare function fromMicroCents(microCents: number): number;
|
package/dist/operations/cost.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export class InvalidCostError extends Error {
|
|
2
|
-
constructor(message) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'InvalidCostError';
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
function assertIntegerMicroCents(value, field) {
|
|
8
|
-
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
9
|
-
throw new InvalidCostError(`${field} must be an integer number of micro-cents, received ${value}. ` +
|
|
10
|
-
'Convert before recording — never round.');
|
|
11
|
-
}
|
|
12
|
-
if (value < 0)
|
|
13
|
-
throw new InvalidCostError(`${field} must not be negative, received ${value}`);
|
|
14
|
-
}
|
|
15
|
-
export function assertValidCost(cost) {
|
|
16
|
-
assertIntegerMicroCents(cost.usdMicroCents, 'usdMicroCents');
|
|
17
|
-
assertIntegerMicroCents(cost.chargedMicroCents, 'chargedMicroCents');
|
|
18
|
-
if (typeof cost.currency !== 'string' || cost.currency.trim() === '') {
|
|
19
|
-
throw new InvalidCostError('cost.currency is required and has no default — resolve it from real data (DATA.md §8)');
|
|
20
|
-
}
|
|
21
|
-
if (!/^[A-Z]{3}$/.test(cost.currency)) {
|
|
22
|
-
throw new InvalidCostError(`cost.currency must be an ISO-4217 code, received "${cost.currency}"`);
|
|
23
|
-
}
|
|
24
|
-
if (typeof cost.meter !== 'string' || cost.meter.trim() === '') {
|
|
25
|
-
throw new InvalidCostError('cost.meter is required — a cost with no meter cannot be quota-checked');
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export const MICRO_CENTS_PER_CENT = 1_000_000;
|
|
29
|
-
export const MICRO_CENTS_PER_UNIT = 100_000_000;
|
|
30
|
-
export function toMicroCents(amount) {
|
|
31
|
-
if (!Number.isFinite(amount))
|
|
32
|
-
throw new InvalidCostError(`cannot convert ${amount} to micro-cents`);
|
|
33
|
-
return Math.round(amount * MICRO_CENTS_PER_UNIT);
|
|
34
|
-
}
|
|
35
|
-
export function fromMicroCents(microCents) {
|
|
36
|
-
return microCents / MICRO_CENTS_PER_UNIT;
|
|
37
|
-
}
|
package/dist/operations/cost.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export interface CostAmount {
|
|
2
|
-
|
|
3
|
-
readonly usdMicroCents: number
|
|
4
|
-
|
|
5
|
-
readonly chargedMicroCents: number
|
|
6
|
-
|
|
7
|
-
readonly currency: string
|
|
8
|
-
|
|
9
|
-
readonly meter: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class InvalidCostError extends Error {
|
|
13
|
-
constructor(message: string) {
|
|
14
|
-
super(message)
|
|
15
|
-
this.name = 'InvalidCostError'
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function assertIntegerMicroCents(value: number, field: string): void {
|
|
20
|
-
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
21
|
-
|
|
22
|
-
throw new InvalidCostError(
|
|
23
|
-
`${field} must be an integer number of micro-cents, received ${value}. ` +
|
|
24
|
-
'Convert before recording — never round.',
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
if (value < 0) throw new InvalidCostError(`${field} must not be negative, received ${value}`)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function assertValidCost(cost: CostAmount): void {
|
|
31
|
-
assertIntegerMicroCents(cost.usdMicroCents, 'usdMicroCents')
|
|
32
|
-
assertIntegerMicroCents(cost.chargedMicroCents, 'chargedMicroCents')
|
|
33
|
-
|
|
34
|
-
if (typeof cost.currency !== 'string' || cost.currency.trim() === '') {
|
|
35
|
-
throw new InvalidCostError(
|
|
36
|
-
'cost.currency is required and has no default — resolve it from real data (DATA.md §8)',
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
if (!/^[A-Z]{3}$/.test(cost.currency)) {
|
|
40
|
-
throw new InvalidCostError(`cost.currency must be an ISO-4217 code, received "${cost.currency}"`)
|
|
41
|
-
}
|
|
42
|
-
if (typeof cost.meter !== 'string' || cost.meter.trim() === '') {
|
|
43
|
-
throw new InvalidCostError('cost.meter is required — a cost with no meter cannot be quota-checked')
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const MICRO_CENTS_PER_CENT = 1_000_000
|
|
48
|
-
|
|
49
|
-
export const MICRO_CENTS_PER_UNIT = 100_000_000
|
|
50
|
-
|
|
51
|
-
export function toMicroCents(amount: number): number {
|
|
52
|
-
if (!Number.isFinite(amount)) throw new InvalidCostError(`cannot convert ${amount} to micro-cents`)
|
|
53
|
-
return Math.round(amount * MICRO_CENTS_PER_UNIT)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function fromMicroCents(microCents: number): number {
|
|
57
|
-
return microCents / MICRO_CENTS_PER_UNIT
|
|
58
|
-
}
|