@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,247 +0,0 @@
|
|
|
1
|
-
import { PlatformError } from "./errors/catalog.js";
|
|
2
|
-
import { DbRoundTripBudgetExceeded } from "./errors/errors.js";
|
|
3
|
-
import { isTransactional } from "./seams.js";
|
|
4
|
-
export class RoundTripBudgetExceededError extends PlatformError {
|
|
5
|
-
budget;
|
|
6
|
-
refusedStatement;
|
|
7
|
-
constructor(budget, statement) {
|
|
8
|
-
const detail = `this operation declared a budget of ${budget} database round trip(s) and asked for ` +
|
|
9
|
-
`"${statement}" as number ${budget + 1}`;
|
|
10
|
-
super(DbRoundTripBudgetExceeded.def, { detail, because: BUDGET_EXCEEDED_BECAUSE });
|
|
11
|
-
this.name = 'RoundTripBudgetExceededError';
|
|
12
|
-
this.message = detail;
|
|
13
|
-
this.budget = budget;
|
|
14
|
-
this.refusedStatement = statement;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export const BUDGET_EXCEEDED_BECAUSE = 'round_trip_budget_exceeded';
|
|
18
|
-
export const DB_USAGE_PROPERTY = 'db';
|
|
19
|
-
export const DB_USAGE_ATTRIBUTES = {
|
|
20
|
-
roundTrips: 'thread.db.round_trips',
|
|
21
|
-
rowsRead: 'thread.db.rows_read',
|
|
22
|
-
rowsWritten: 'thread.db.rows_written',
|
|
23
|
-
bytesRead: 'thread.db.bytes_read',
|
|
24
|
-
bytesWritten: 'thread.db.bytes_written',
|
|
25
|
-
budgetRoundTrips: 'thread.db.budget_round_trips',
|
|
26
|
-
};
|
|
27
|
-
export const DB_AMPLIFICATION_PROPERTY = 'db_amplification';
|
|
28
|
-
export const DB_AMPLIFICATION_ATTRIBUTES = {
|
|
29
|
-
entities: 'thread.db.amplified_entities',
|
|
30
|
-
columns: 'thread.db.amplified_columns',
|
|
31
|
-
bytes: 'thread.db.amplified_bytes',
|
|
32
|
-
writes: 'thread.db.amplified_writes',
|
|
33
|
-
};
|
|
34
|
-
export function databaseUsageValues(usage, budget) {
|
|
35
|
-
return {
|
|
36
|
-
[DB_USAGE_ATTRIBUTES.roundTrips]: usage.roundTrips,
|
|
37
|
-
[DB_USAGE_ATTRIBUTES.rowsRead]: usage.rowsRead,
|
|
38
|
-
[DB_USAGE_ATTRIBUTES.rowsWritten]: usage.rowsWritten,
|
|
39
|
-
[DB_USAGE_ATTRIBUTES.bytesRead]: usage.bytesRead,
|
|
40
|
-
[DB_USAGE_ATTRIBUTES.bytesWritten]: usage.bytesWritten,
|
|
41
|
-
...(budget === undefined ? {} : { [DB_USAGE_ATTRIBUTES.budgetRoundTrips]: budget.roundTrips }),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
export function writeAmplificationValues(usage) {
|
|
45
|
-
if (usage.amplified.length === 0)
|
|
46
|
-
return undefined;
|
|
47
|
-
const entities = [...new Set(usage.amplified.map((write) => write.entity))];
|
|
48
|
-
const columns = [...new Set(usage.amplified.flatMap((write) => write.columns))];
|
|
49
|
-
return {
|
|
50
|
-
[DB_AMPLIFICATION_ATTRIBUTES.entities]: entities.join(','),
|
|
51
|
-
[DB_AMPLIFICATION_ATTRIBUTES.columns]: columns.join(','),
|
|
52
|
-
[DB_AMPLIFICATION_ATTRIBUTES.bytes]: usage.amplified.reduce((total, write) => total + write.bytes, 0),
|
|
53
|
-
[DB_AMPLIFICATION_ATTRIBUTES.writes]: usage.amplified.length,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
const MAX_WALK_DEPTH = 24;
|
|
57
|
-
function utf8Length(text) {
|
|
58
|
-
let bytes = 0;
|
|
59
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
60
|
-
const code = text.charCodeAt(index);
|
|
61
|
-
if (code < 0x80)
|
|
62
|
-
bytes += 1;
|
|
63
|
-
else if (code < 0x800)
|
|
64
|
-
bytes += 2;
|
|
65
|
-
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < text.length) {
|
|
66
|
-
bytes += 4;
|
|
67
|
-
index += 1;
|
|
68
|
-
}
|
|
69
|
-
else
|
|
70
|
-
bytes += 3;
|
|
71
|
-
}
|
|
72
|
-
return bytes;
|
|
73
|
-
}
|
|
74
|
-
export function payloadBytes(value, depth = 0) {
|
|
75
|
-
if (value === null || value === undefined)
|
|
76
|
-
return 0;
|
|
77
|
-
if (depth > MAX_WALK_DEPTH)
|
|
78
|
-
return 0;
|
|
79
|
-
switch (typeof value) {
|
|
80
|
-
case 'string':
|
|
81
|
-
return utf8Length(value);
|
|
82
|
-
case 'number':
|
|
83
|
-
case 'boolean':
|
|
84
|
-
case 'bigint':
|
|
85
|
-
return 8;
|
|
86
|
-
case 'object':
|
|
87
|
-
break;
|
|
88
|
-
default:
|
|
89
|
-
return 0;
|
|
90
|
-
}
|
|
91
|
-
if (value instanceof Date)
|
|
92
|
-
return 8;
|
|
93
|
-
if (value instanceof Uint8Array)
|
|
94
|
-
return value.byteLength;
|
|
95
|
-
if (Array.isArray(value)) {
|
|
96
|
-
let total = 0;
|
|
97
|
-
for (const item of value)
|
|
98
|
-
total += payloadBytes(item, depth + 1);
|
|
99
|
-
return total;
|
|
100
|
-
}
|
|
101
|
-
let total = 0;
|
|
102
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
103
|
-
total += utf8Length(key) + payloadBytes(nested, depth + 1);
|
|
104
|
-
}
|
|
105
|
-
return total;
|
|
106
|
-
}
|
|
107
|
-
function sameStoredValue(left, right, depth = 0) {
|
|
108
|
-
if (left === right)
|
|
109
|
-
return true;
|
|
110
|
-
if (depth > MAX_WALK_DEPTH)
|
|
111
|
-
return false;
|
|
112
|
-
if (left instanceof Date || right instanceof Date) {
|
|
113
|
-
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
114
|
-
}
|
|
115
|
-
if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null)
|
|
116
|
-
return false;
|
|
117
|
-
if (Array.isArray(left) !== Array.isArray(right))
|
|
118
|
-
return false;
|
|
119
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
120
|
-
return left.length === right.length && left.every((item, index) => sameStoredValue(item, right[index], depth + 1));
|
|
121
|
-
}
|
|
122
|
-
const a = left;
|
|
123
|
-
const b = right;
|
|
124
|
-
const keys = Object.keys(a);
|
|
125
|
-
if (keys.length !== Object.keys(b).length)
|
|
126
|
-
return false;
|
|
127
|
-
return keys.every((key) => key in b && sameStoredValue(a[key], b[key], depth + 1));
|
|
128
|
-
}
|
|
129
|
-
export function meterDatabase(seams, budget) {
|
|
130
|
-
let roundTrips = 0;
|
|
131
|
-
let rowsRead = 0;
|
|
132
|
-
let rowsWritten = 0;
|
|
133
|
-
let bytesRead = 0;
|
|
134
|
-
let bytesWritten = 0;
|
|
135
|
-
const amplified = [];
|
|
136
|
-
const seen = new Map();
|
|
137
|
-
const remember = (entity, row) => {
|
|
138
|
-
seen.set(`${entity}:${row.id}`, row);
|
|
139
|
-
};
|
|
140
|
-
const spend = (statement) => {
|
|
141
|
-
if (budget !== undefined && roundTrips >= budget.roundTrips) {
|
|
142
|
-
throw new RoundTripBudgetExceededError(budget.roundTrips, statement);
|
|
143
|
-
}
|
|
144
|
-
roundTrips += 1;
|
|
145
|
-
};
|
|
146
|
-
const readRows = (rows) => {
|
|
147
|
-
rowsRead += rows.length;
|
|
148
|
-
for (const row of rows)
|
|
149
|
-
bytesRead += payloadBytes(row);
|
|
150
|
-
};
|
|
151
|
-
const base = seams.store;
|
|
152
|
-
const store = base === undefined ? undefined : {
|
|
153
|
-
async insert(actor, entity, row) {
|
|
154
|
-
spend(`insert ${entity}`);
|
|
155
|
-
bytesWritten += payloadBytes(row);
|
|
156
|
-
rowsWritten += 1;
|
|
157
|
-
const written = await base.insert(actor, entity, row);
|
|
158
|
-
remember(entity, written);
|
|
159
|
-
return written;
|
|
160
|
-
},
|
|
161
|
-
async get(actor, entity, id, fields) {
|
|
162
|
-
spend(`get ${entity}`);
|
|
163
|
-
const row = await base.get(actor, entity, id, fields);
|
|
164
|
-
if (row !== null) {
|
|
165
|
-
rowsRead += 1;
|
|
166
|
-
bytesRead += payloadBytes(row);
|
|
167
|
-
remember(entity, row);
|
|
168
|
-
}
|
|
169
|
-
return row;
|
|
170
|
-
},
|
|
171
|
-
async list(actor, entity, opts) {
|
|
172
|
-
spend(`list ${entity}`);
|
|
173
|
-
const rows = await base.list(actor, entity, opts);
|
|
174
|
-
readRows(rows);
|
|
175
|
-
for (const row of rows)
|
|
176
|
-
remember(entity, row);
|
|
177
|
-
return rows;
|
|
178
|
-
},
|
|
179
|
-
async update(actor, entity, id, patch, expectedVersion) {
|
|
180
|
-
spend(`update ${entity}`);
|
|
181
|
-
recordAmplification(entity, id, patch);
|
|
182
|
-
bytesWritten += payloadBytes(patch);
|
|
183
|
-
rowsWritten += 1;
|
|
184
|
-
const row = await base.update(actor, entity, id, patch, expectedVersion);
|
|
185
|
-
remember(entity, row);
|
|
186
|
-
return row;
|
|
187
|
-
},
|
|
188
|
-
async remove(actor, entity, id) {
|
|
189
|
-
spend(`remove ${entity}`);
|
|
190
|
-
rowsWritten += 1;
|
|
191
|
-
await base.remove(actor, entity, id);
|
|
192
|
-
seen.delete(`${entity}:${id}`);
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
if (base !== undefined && store !== undefined && isTransactional(base)) {
|
|
196
|
-
store.transaction = (actor, fn) => base.transaction(actor, fn);
|
|
197
|
-
}
|
|
198
|
-
function recordAmplification(entity, id, patch) {
|
|
199
|
-
const stored = seen.get(`${entity}:${id}`);
|
|
200
|
-
if (stored === undefined)
|
|
201
|
-
return;
|
|
202
|
-
const columns = [];
|
|
203
|
-
let bytes = 0;
|
|
204
|
-
for (const [column, value] of Object.entries(patch)) {
|
|
205
|
-
if (!(column in stored))
|
|
206
|
-
continue;
|
|
207
|
-
if (!sameStoredValue(value, stored[column]))
|
|
208
|
-
continue;
|
|
209
|
-
columns.push(column);
|
|
210
|
-
bytes += payloadBytes(value);
|
|
211
|
-
}
|
|
212
|
-
if (columns.length === 0)
|
|
213
|
-
return;
|
|
214
|
-
amplified.push({ entity, id: String(id), columns, bytes });
|
|
215
|
-
}
|
|
216
|
-
const views = seams.views === undefined
|
|
217
|
-
? undefined
|
|
218
|
-
: {
|
|
219
|
-
async query(actor, sql, params) {
|
|
220
|
-
spend('view');
|
|
221
|
-
const rows = await seams.views.query(actor, sql, params);
|
|
222
|
-
readRows(rows);
|
|
223
|
-
return rows;
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
const baseSearch = seams.search;
|
|
227
|
-
const search = baseSearch === undefined
|
|
228
|
-
? undefined
|
|
229
|
-
: {
|
|
230
|
-
index: (actor, entity, row) => baseSearch.index(actor, entity, row),
|
|
231
|
-
async query(actor, entity, q) {
|
|
232
|
-
spend(`search ${entity}`);
|
|
233
|
-
const rows = await baseSearch.query(actor, entity, q);
|
|
234
|
-
readRows(rows);
|
|
235
|
-
return rows;
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
const metered = {
|
|
239
|
-
...(store === undefined ? {} : { store }),
|
|
240
|
-
...(search === undefined ? {} : { search }),
|
|
241
|
-
...(views === undefined ? {} : { views }),
|
|
242
|
-
};
|
|
243
|
-
return {
|
|
244
|
-
seams: { ...seams, ...metered },
|
|
245
|
-
usage: () => ({ roundTrips, rowsRead, rowsWritten, bytesRead, bytesWritten, amplified }),
|
|
246
|
-
};
|
|
247
|
-
}
|
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
import { PlatformError } from './errors/catalog.ts'
|
|
2
|
-
import { DbRoundTripBudgetExceeded } from './errors/errors.ts'
|
|
3
|
-
|
|
4
|
-
import type { Actor } from '../core/actor.ts'
|
|
5
|
-
import type { PropertyValue, Row, Search, Seams, Store, Transactional, Views } from './seams.ts'
|
|
6
|
-
import { isTransactional } from './seams.ts'
|
|
7
|
-
|
|
8
|
-
export interface ActionBudget {
|
|
9
|
-
|
|
10
|
-
readonly roundTrips: number
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface WriteAmplification {
|
|
14
|
-
readonly entity: string
|
|
15
|
-
readonly id: string
|
|
16
|
-
|
|
17
|
-
readonly columns: readonly string[]
|
|
18
|
-
|
|
19
|
-
readonly bytes: number
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface DatabaseUsage {
|
|
23
|
-
|
|
24
|
-
readonly roundTrips: number
|
|
25
|
-
readonly rowsRead: number
|
|
26
|
-
readonly rowsWritten: number
|
|
27
|
-
readonly bytesRead: number
|
|
28
|
-
readonly bytesWritten: number
|
|
29
|
-
|
|
30
|
-
readonly amplified: readonly WriteAmplification[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class RoundTripBudgetExceededError extends PlatformError {
|
|
34
|
-
readonly budget: number
|
|
35
|
-
|
|
36
|
-
readonly refusedStatement: string
|
|
37
|
-
|
|
38
|
-
constructor(budget: number, statement: string) {
|
|
39
|
-
const detail =
|
|
40
|
-
`this operation declared a budget of ${budget} database round trip(s) and asked for ` +
|
|
41
|
-
`"${statement}" as number ${budget + 1}`
|
|
42
|
-
|
|
43
|
-
super(DbRoundTripBudgetExceeded.def, { detail, because: BUDGET_EXCEEDED_BECAUSE })
|
|
44
|
-
this.name = 'RoundTripBudgetExceededError'
|
|
45
|
-
|
|
46
|
-
this.message = detail
|
|
47
|
-
this.budget = budget
|
|
48
|
-
this.refusedStatement = statement
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const BUDGET_EXCEEDED_BECAUSE = 'round_trip_budget_exceeded'
|
|
53
|
-
|
|
54
|
-
export const DB_USAGE_PROPERTY = 'db'
|
|
55
|
-
|
|
56
|
-
export const DB_USAGE_ATTRIBUTES = {
|
|
57
|
-
roundTrips: 'thread.db.round_trips',
|
|
58
|
-
rowsRead: 'thread.db.rows_read',
|
|
59
|
-
rowsWritten: 'thread.db.rows_written',
|
|
60
|
-
bytesRead: 'thread.db.bytes_read',
|
|
61
|
-
bytesWritten: 'thread.db.bytes_written',
|
|
62
|
-
budgetRoundTrips: 'thread.db.budget_round_trips',
|
|
63
|
-
} as const
|
|
64
|
-
|
|
65
|
-
export const DB_AMPLIFICATION_PROPERTY = 'db_amplification'
|
|
66
|
-
|
|
67
|
-
export const DB_AMPLIFICATION_ATTRIBUTES = {
|
|
68
|
-
entities: 'thread.db.amplified_entities',
|
|
69
|
-
columns: 'thread.db.amplified_columns',
|
|
70
|
-
bytes: 'thread.db.amplified_bytes',
|
|
71
|
-
writes: 'thread.db.amplified_writes',
|
|
72
|
-
} as const
|
|
73
|
-
|
|
74
|
-
export function databaseUsageValues(
|
|
75
|
-
usage: DatabaseUsage,
|
|
76
|
-
budget?: ActionBudget,
|
|
77
|
-
): Readonly<Record<string, PropertyValue>> {
|
|
78
|
-
return {
|
|
79
|
-
[DB_USAGE_ATTRIBUTES.roundTrips]: usage.roundTrips,
|
|
80
|
-
[DB_USAGE_ATTRIBUTES.rowsRead]: usage.rowsRead,
|
|
81
|
-
[DB_USAGE_ATTRIBUTES.rowsWritten]: usage.rowsWritten,
|
|
82
|
-
[DB_USAGE_ATTRIBUTES.bytesRead]: usage.bytesRead,
|
|
83
|
-
[DB_USAGE_ATTRIBUTES.bytesWritten]: usage.bytesWritten,
|
|
84
|
-
...(budget === undefined ? {} : { [DB_USAGE_ATTRIBUTES.budgetRoundTrips]: budget.roundTrips }),
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function writeAmplificationValues(
|
|
89
|
-
usage: DatabaseUsage,
|
|
90
|
-
): Readonly<Record<string, PropertyValue>> | undefined {
|
|
91
|
-
if (usage.amplified.length === 0) return undefined
|
|
92
|
-
|
|
93
|
-
const entities = [...new Set(usage.amplified.map((write) => write.entity))]
|
|
94
|
-
const columns = [...new Set(usage.amplified.flatMap((write) => write.columns))]
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
[DB_AMPLIFICATION_ATTRIBUTES.entities]: entities.join(','),
|
|
98
|
-
[DB_AMPLIFICATION_ATTRIBUTES.columns]: columns.join(','),
|
|
99
|
-
[DB_AMPLIFICATION_ATTRIBUTES.bytes]: usage.amplified.reduce((total, write) => total + write.bytes, 0),
|
|
100
|
-
[DB_AMPLIFICATION_ATTRIBUTES.writes]: usage.amplified.length,
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const MAX_WALK_DEPTH = 24
|
|
105
|
-
|
|
106
|
-
function utf8Length(text: string): number {
|
|
107
|
-
let bytes = 0
|
|
108
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
109
|
-
const code = text.charCodeAt(index)
|
|
110
|
-
if (code < 0x80) bytes += 1
|
|
111
|
-
else if (code < 0x800) bytes += 2
|
|
112
|
-
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < text.length) {
|
|
113
|
-
|
|
114
|
-
bytes += 4
|
|
115
|
-
index += 1
|
|
116
|
-
} else bytes += 3
|
|
117
|
-
}
|
|
118
|
-
return bytes
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function payloadBytes(value: unknown, depth = 0): number {
|
|
122
|
-
if (value === null || value === undefined) return 0
|
|
123
|
-
if (depth > MAX_WALK_DEPTH) return 0
|
|
124
|
-
|
|
125
|
-
switch (typeof value) {
|
|
126
|
-
case 'string':
|
|
127
|
-
return utf8Length(value)
|
|
128
|
-
case 'number':
|
|
129
|
-
case 'boolean':
|
|
130
|
-
case 'bigint':
|
|
131
|
-
|
|
132
|
-
return 8
|
|
133
|
-
case 'object':
|
|
134
|
-
break
|
|
135
|
-
default:
|
|
136
|
-
|
|
137
|
-
return 0
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (value instanceof Date) return 8
|
|
141
|
-
if (value instanceof Uint8Array) return value.byteLength
|
|
142
|
-
if (Array.isArray(value)) {
|
|
143
|
-
let total = 0
|
|
144
|
-
for (const item of value) total += payloadBytes(item, depth + 1)
|
|
145
|
-
return total
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
let total = 0
|
|
149
|
-
for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
|
|
150
|
-
|
|
151
|
-
total += utf8Length(key) + payloadBytes(nested, depth + 1)
|
|
152
|
-
}
|
|
153
|
-
return total
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function sameStoredValue(left: unknown, right: unknown, depth = 0): boolean {
|
|
157
|
-
if (left === right) return true
|
|
158
|
-
if (depth > MAX_WALK_DEPTH) return false
|
|
159
|
-
|
|
160
|
-
if (left instanceof Date || right instanceof Date) {
|
|
161
|
-
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime()
|
|
162
|
-
}
|
|
163
|
-
if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) return false
|
|
164
|
-
if (Array.isArray(left) !== Array.isArray(right)) return false
|
|
165
|
-
|
|
166
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
167
|
-
return left.length === right.length && left.every((item, index) => sameStoredValue(item, right[index], depth + 1))
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const a = left as Record<string, unknown>
|
|
171
|
-
const b = right as Record<string, unknown>
|
|
172
|
-
const keys = Object.keys(a)
|
|
173
|
-
if (keys.length !== Object.keys(b).length) return false
|
|
174
|
-
return keys.every((key) => key in b && sameStoredValue(a[key], b[key], depth + 1))
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export interface MeterableSeams {
|
|
178
|
-
readonly store?: Store
|
|
179
|
-
readonly search?: Search
|
|
180
|
-
readonly views?: Views
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export interface Metered<S> {
|
|
184
|
-
readonly seams: S
|
|
185
|
-
|
|
186
|
-
usage(): DatabaseUsage
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export type MeteredSeams = Metered<Seams>
|
|
190
|
-
|
|
191
|
-
export function meterDatabase<S extends MeterableSeams>(seams: S, budget?: ActionBudget): Metered<S> {
|
|
192
|
-
let roundTrips = 0
|
|
193
|
-
let rowsRead = 0
|
|
194
|
-
let rowsWritten = 0
|
|
195
|
-
let bytesRead = 0
|
|
196
|
-
let bytesWritten = 0
|
|
197
|
-
const amplified: WriteAmplification[] = []
|
|
198
|
-
|
|
199
|
-
const seen = new Map<string, Row>()
|
|
200
|
-
const remember = (entity: string, row: Row): void => {
|
|
201
|
-
seen.set(`${entity}:${row.id}`, row)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const spend = (statement: string): void => {
|
|
205
|
-
if (budget !== undefined && roundTrips >= budget.roundTrips) {
|
|
206
|
-
|
|
207
|
-
throw new RoundTripBudgetExceededError(budget.roundTrips, statement)
|
|
208
|
-
}
|
|
209
|
-
roundTrips += 1
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const readRows = (rows: readonly unknown[]): void => {
|
|
213
|
-
rowsRead += rows.length
|
|
214
|
-
for (const row of rows) bytesRead += payloadBytes(row)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const base = seams.store
|
|
218
|
-
|
|
219
|
-
const store: (Store & Partial<Transactional>) | undefined = base === undefined ? undefined : {
|
|
220
|
-
async insert(actor, entity, row) {
|
|
221
|
-
spend(`insert ${entity}`)
|
|
222
|
-
bytesWritten += payloadBytes(row)
|
|
223
|
-
rowsWritten += 1
|
|
224
|
-
const written = await base.insert(actor, entity, row)
|
|
225
|
-
remember(entity, written)
|
|
226
|
-
return written
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
async get(actor, entity, id, fields) {
|
|
230
|
-
spend(`get ${entity}`)
|
|
231
|
-
const row = await base.get(actor, entity, id, fields)
|
|
232
|
-
if (row !== null) {
|
|
233
|
-
rowsRead += 1
|
|
234
|
-
bytesRead += payloadBytes(row)
|
|
235
|
-
remember(entity, row)
|
|
236
|
-
}
|
|
237
|
-
return row
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
async list(actor, entity, opts) {
|
|
241
|
-
spend(`list ${entity}`)
|
|
242
|
-
const rows = await base.list(actor, entity, opts)
|
|
243
|
-
readRows(rows)
|
|
244
|
-
for (const row of rows) remember(entity, row)
|
|
245
|
-
return rows
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
async update(actor, entity, id, patch, expectedVersion) {
|
|
249
|
-
spend(`update ${entity}`)
|
|
250
|
-
recordAmplification(entity, id, patch)
|
|
251
|
-
bytesWritten += payloadBytes(patch)
|
|
252
|
-
rowsWritten += 1
|
|
253
|
-
const row = await base.update(actor, entity, id, patch, expectedVersion)
|
|
254
|
-
remember(entity, row)
|
|
255
|
-
return row
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
async remove(actor, entity, id) {
|
|
259
|
-
|
|
260
|
-
spend(`remove ${entity}`)
|
|
261
|
-
rowsWritten += 1
|
|
262
|
-
await base.remove(actor, entity, id)
|
|
263
|
-
seen.delete(`${entity}:${id}`)
|
|
264
|
-
},
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (base !== undefined && store !== undefined && isTransactional(base)) {
|
|
268
|
-
store.transaction = (actor, fn) => base.transaction(actor, fn)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function recordAmplification(entity: string, id: string | number, patch: Record<string, unknown>): void {
|
|
272
|
-
const stored = seen.get(`${entity}:${id}`)
|
|
273
|
-
if (stored === undefined) return
|
|
274
|
-
|
|
275
|
-
const columns: string[] = []
|
|
276
|
-
let bytes = 0
|
|
277
|
-
for (const [column, value] of Object.entries(patch)) {
|
|
278
|
-
if (!(column in stored)) continue
|
|
279
|
-
if (!sameStoredValue(value, stored[column])) continue
|
|
280
|
-
columns.push(column)
|
|
281
|
-
bytes += payloadBytes(value)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (columns.length === 0) return
|
|
285
|
-
amplified.push({ entity, id: String(id), columns, bytes })
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const views: Views | undefined =
|
|
289
|
-
seams.views === undefined
|
|
290
|
-
? undefined
|
|
291
|
-
: {
|
|
292
|
-
async query<T = Record<string, unknown>>(
|
|
293
|
-
actor: Actor,
|
|
294
|
-
sql: string,
|
|
295
|
-
params: readonly unknown[],
|
|
296
|
-
): Promise<readonly T[]> {
|
|
297
|
-
spend('view')
|
|
298
|
-
const rows = await (seams.views as Views).query<T>(actor, sql, params)
|
|
299
|
-
readRows(rows)
|
|
300
|
-
return rows
|
|
301
|
-
},
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const baseSearch = seams.search
|
|
305
|
-
const search: Search | undefined =
|
|
306
|
-
baseSearch === undefined
|
|
307
|
-
? undefined
|
|
308
|
-
: {
|
|
309
|
-
index: (actor, entity, row) => baseSearch.index(actor, entity, row),
|
|
310
|
-
async query(actor, entity, q) {
|
|
311
|
-
spend(`search ${entity}`)
|
|
312
|
-
const rows = await baseSearch.query(actor, entity, q)
|
|
313
|
-
readRows(rows)
|
|
314
|
-
return rows
|
|
315
|
-
},
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const metered: MeterableSeams = {
|
|
319
|
-
...(store === undefined ? {} : { store }),
|
|
320
|
-
...(search === undefined ? {} : { search }),
|
|
321
|
-
...(views === undefined ? {} : { views }),
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
seams: { ...seams, ...metered } as S,
|
|
326
|
-
usage: () => ({ roundTrips, rowsRead, rowsWritten, bytesRead, bytesWritten, amplified }),
|
|
327
|
-
}
|
|
328
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const MAX_ATTEMPTS = 5
|
|
2
|
-
|
|
3
|
-
export function backoffMs(attempts: number): number {
|
|
4
|
-
const base = 1_000 * 2 ** Math.max(0, attempts - 1)
|
|
5
|
-
return Math.min(base, 5 * 60_000)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function isExhausted(attempts: number): boolean {
|
|
9
|
-
return attempts >= MAX_ATTEMPTS
|
|
10
|
-
}
|