@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.
Files changed (270) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/dist/core/actor.d.ts +1 -1
  3. package/dist/core/actor.js +5 -4
  4. package/dist/core/actor.ts +5 -4
  5. package/dist/core/attach.d.ts +0 -7
  6. package/dist/core/attach.js +38 -67
  7. package/dist/core/attach.ts +34 -80
  8. package/dist/core/chain.js +13 -17
  9. package/dist/core/chain.ts +11 -15
  10. package/dist/core/condition.d.ts +4 -4
  11. package/dist/core/condition.js +2 -2
  12. package/dist/core/descend.d.ts +4 -2
  13. package/dist/core/descend.js +91 -23
  14. package/dist/core/descend.ts +90 -21
  15. package/dist/core/errors/catalogue.d.ts +52 -0
  16. package/dist/{operations/errors/errors.js → core/errors/catalogue.js} +1 -1
  17. package/dist/{operations/errors/errors.ts → core/errors/catalogue.ts} +1 -1
  18. package/dist/{operations/errors/catalog.d.ts → core/errors/error.d.ts} +2 -3
  19. package/dist/{operations/errors/catalog.js → core/errors/error.js} +1 -16
  20. package/dist/{operations/errors/catalog.ts → core/errors/error.ts} +3 -19
  21. package/dist/core/errors/namespaces.d.ts +3 -0
  22. package/dist/core/errors/namespaces.js +15 -0
  23. package/dist/core/errors/namespaces.ts +19 -0
  24. package/dist/core/finding.js +2 -2
  25. package/dist/core/finding.ts +1 -2
  26. package/dist/core/init.d.ts +1 -1
  27. package/dist/core/init.js +51 -5
  28. package/dist/core/init.ts +58 -5
  29. package/dist/core/invoked.d.ts +1 -1
  30. package/dist/core/invoked.js +3 -1
  31. package/dist/core/invoked.ts +5 -2
  32. package/dist/core/round.js +36 -22
  33. package/dist/core/round.ts +26 -9
  34. package/dist/core/rows.d.ts +4 -0
  35. package/dist/core/rows.js +31 -2
  36. package/dist/core/rows.ts +33 -1
  37. package/dist/core/scope.d.ts +1 -0
  38. package/dist/core/scope.js +1 -0
  39. package/dist/core/scope.ts +2 -0
  40. package/dist/core/thread.d.ts +3 -3
  41. package/dist/core/tree.d.ts +4 -1
  42. package/dist/core/tree.js +26 -6
  43. package/dist/core/tree.ts +29 -6
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.js +3 -1
  46. package/dist/core/types.ts +2 -1
  47. package/dist/derivation/attach.js +3 -2
  48. package/dist/derivation/attach.ts +2 -1
  49. package/dist/derivation/generators/build.js +6 -6
  50. package/dist/derivation/generators/build.ts +1 -1
  51. package/dist/derivation/generators/check.js +1 -1
  52. package/dist/derivation/generators/determinism.js +1 -1
  53. package/dist/derivation/generators/release.js +60 -47
  54. package/dist/derivation/generators/release.ts +66 -43
  55. package/dist/derivation/release.d.ts +7 -3
  56. package/dist/derivation/release.js +14 -1
  57. package/dist/derivation/release.ts +22 -2
  58. package/dist/integrations/attach.d.ts +4 -0
  59. package/dist/integrations/attach.js +37 -56
  60. package/dist/integrations/attach.ts +54 -60
  61. package/dist/integrations/cli.d.ts +37 -0
  62. package/dist/integrations/cli.js +440 -0
  63. package/dist/integrations/cli.ts +543 -0
  64. package/dist/{core/files.d.ts → integrations/disk.d.ts} +16 -24
  65. package/dist/integrations/disk.js +435 -0
  66. package/dist/integrations/disk.ts +465 -0
  67. package/dist/integrations/generators/read.js +6 -6
  68. package/dist/integrations/generators/read.ts +4 -4
  69. package/dist/integrations/generators/vault.js +10 -11
  70. package/dist/integrations/generators/vault.ts +4 -5
  71. package/dist/{operations/http.d.ts → integrations/https.d.ts} +10 -6
  72. package/dist/integrations/https.js +170 -0
  73. package/dist/integrations/https.ts +216 -0
  74. package/dist/integrations/pglite.d.ts +26 -0
  75. package/dist/integrations/pglite.js +143 -0
  76. package/dist/integrations/pglite.ts +206 -0
  77. package/dist/integrations/secret.d.ts +8 -2
  78. package/dist/integrations/secret.js +3 -5
  79. package/dist/integrations/secret.ts +10 -6
  80. package/dist/integrations/target.d.ts +155 -14
  81. package/dist/integrations/target.js +171 -50
  82. package/dist/integrations/target.ts +377 -61
  83. package/dist/integrations/vault.d.ts +1 -0
  84. package/dist/integrations/vault.js +20 -5
  85. package/dist/integrations/vault.ts +17 -5
  86. package/dist/integrity/attach.js +8 -77
  87. package/dist/integrity/attach.ts +7 -76
  88. package/dist/integrity/check-threads.js +5 -5
  89. package/dist/integrity/check-threads.ts +2 -2
  90. package/dist/integrity/constraint.d.ts +0 -1
  91. package/dist/integrity/constraint.js +267 -366
  92. package/dist/integrity/constraint.ts +267 -375
  93. package/dist/integrity/generators/compiler.js +14 -17
  94. package/dist/integrity/generators/compiler.ts +10 -15
  95. package/dist/integrity/generators/gate.js +15 -15
  96. package/dist/integrity/generators/gate.ts +10 -10
  97. package/dist/integrity/generators/issues.js +7 -7
  98. package/dist/integrity/generators/publish.js +39 -57
  99. package/dist/integrity/generators/publish.ts +44 -55
  100. package/dist/integrity/generators/trial.d.ts +3 -3
  101. package/dist/integrity/generators/trial.js +133 -165
  102. package/dist/integrity/generators/trial.ts +145 -169
  103. package/dist/integrity/lint.js +9 -13
  104. package/dist/integrity/lint.ts +7 -10
  105. package/dist/integrity/lsp/bin.js +5 -4
  106. package/dist/integrity/lsp/bin.ts +3 -1
  107. package/dist/integrity/lsp/diagnostics.js +3 -3
  108. package/dist/integrity/lsp/diagnostics.ts +1 -1
  109. package/dist/integrity/lsp/lint.js +19 -30
  110. package/dist/integrity/lsp/lint.ts +16 -32
  111. package/dist/integrity/lsp/server.js +3 -3
  112. package/dist/integrity/slopnator.d.ts +3 -13
  113. package/dist/integrity/slopnator.js +59 -38
  114. package/dist/integrity/slopnator.ts +54 -38
  115. package/dist/integrity/slopnators/sl_broken_lineage.js +2 -2
  116. package/dist/integrity/slopnators/sl_declared_elsewhere.js +1 -1
  117. package/dist/integrity/slopnators/sl_derived_drift.js +4 -4
  118. package/dist/integrity/slopnators/sl_derived_drift.ts +1 -1
  119. package/dist/integrity/slopnators/sl_door_in_a_node.js +220 -0
  120. package/dist/integrity/slopnators/sl_door_in_a_node.ts +250 -0
  121. package/dist/integrity/slopnators/sl_inert_island.js +10 -8
  122. package/dist/integrity/slopnators/sl_inert_island.ts +8 -6
  123. package/dist/integrity/slopnators/sl_missing_row.js +6 -51
  124. package/dist/integrity/slopnators/sl_missing_row.ts +4 -50
  125. package/dist/integrity/slopnators/sl_no_barrel.js +1 -1
  126. package/dist/integrity/slopnators/sl_no_child_process.js +36 -18
  127. package/dist/integrity/slopnators/sl_no_child_process.ts +44 -18
  128. package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +80 -149
  129. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +95 -147
  130. package/dist/integrity/slopnators/sl_no_import_cycle.js +15 -4
  131. package/dist/integrity/slopnators/sl_no_import_cycle.ts +20 -4
  132. package/dist/integrity/slopnators/sl_no_such_command.js +2 -2
  133. package/dist/integrity/slopnators/sl_no_such_command.ts +1 -1
  134. package/dist/integrity/slopnators/sl_outside_an_operation.js +20 -0
  135. package/dist/integrity/slopnators/sl_outside_an_operation.ts +25 -0
  136. package/dist/integrity/slopnators/sl_screen_holds_only_declared.d.ts +2 -0
  137. package/dist/integrity/slopnators/sl_screen_holds_only_declared.js +61 -0
  138. package/dist/integrity/slopnators/sl_screen_holds_only_declared.ts +70 -0
  139. package/dist/integrity/slopnators/sl_secret_literal.js +1 -33
  140. package/dist/integrity/slopnators/sl_secret_literal.ts +1 -33
  141. package/dist/integrity/slopnators/sl_stale_path.js +5 -22
  142. package/dist/integrity/slopnators/sl_stale_path.ts +4 -20
  143. package/dist/integrity/slopnators/sl_twice_meant.js +1 -1
  144. package/dist/integrity/slopnators/sl_twice_parented.js +29 -5
  145. package/dist/integrity/slopnators/sl_twice_parented.ts +28 -4
  146. package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +4 -1
  147. package/dist/integrity/slopnators/sl_undeclared_crossing.js +426 -62
  148. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +454 -70
  149. package/dist/integrity/slopnators/sl_undeclared_destination.js +59 -25
  150. package/dist/integrity/slopnators/sl_undeclared_destination.ts +69 -21
  151. package/dist/integrity/slopnators/sl_undeclared_list.d.ts +2 -0
  152. package/dist/integrity/slopnators/sl_undeclared_list.js +363 -0
  153. package/dist/integrity/slopnators/sl_undeclared_list.ts +358 -0
  154. package/dist/integrity/slopnators/sl_undeclared_node.js +1 -11
  155. package/dist/integrity/slopnators/sl_undeclared_node.ts +0 -10
  156. package/dist/integrity/slopnators/sl_undeclared_operation.js +111 -29
  157. package/dist/integrity/slopnators/sl_undeclared_operation.ts +134 -26
  158. package/dist/integrity/slopnators/sl_undeclared_variant.js +2 -2
  159. package/dist/integrity/slopnators/sl_undeclared_webhook.d.ts +2 -0
  160. package/dist/integrity/slopnators/sl_undeclared_webhook.js +90 -0
  161. package/dist/integrity/slopnators/sl_undeclared_webhook.ts +108 -0
  162. package/dist/integrity/slopnators/sl_underived_route.d.ts +2 -0
  163. package/dist/integrity/slopnators/sl_underived_route.js +61 -0
  164. package/dist/integrity/slopnators/sl_underived_route.ts +72 -0
  165. package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -3
  166. package/dist/integrity/slopnators/sl_ungoverned_island.ts +1 -1
  167. package/dist/integrity/slopnators/sl_unlisted_program.d.ts +2 -0
  168. package/dist/integrity/slopnators/sl_unlisted_program.js +39 -0
  169. package/dist/integrity/slopnators/sl_unlisted_program.ts +40 -0
  170. package/dist/integrity/slopnators/sl_unnamed_island.js +1 -1
  171. package/dist/integrity/slopnators/sl_unpaired_member.js +3 -3
  172. package/dist/integrity/slopnators/sl_unpaired_member.ts +1 -1
  173. package/dist/integrity/slopnators/sl_unpaired_slopnator.js +4 -4
  174. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +1 -1
  175. package/dist/integrity/slopnators/sl_unprefixed_member.js +5 -31
  176. package/dist/integrity/slopnators/sl_unprefixed_member.ts +2 -28
  177. package/dist/integrity/slopnators/sl_unthreaded.js +11 -7
  178. package/dist/integrity/slopnators/sl_unthreaded.ts +6 -7
  179. package/dist/integrity/slopnators/sl_untranslated_key.js +2 -25
  180. package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -25
  181. package/dist/integrity/slopnators/sl_unvaulted_secret.d.ts +2 -0
  182. package/dist/integrity/slopnators/sl_unvaulted_secret.js +107 -0
  183. package/dist/integrity/slopnators/sl_unvaulted_secret.ts +132 -0
  184. package/dist/integrity/slopnators/sl_unversioned.js +6 -4
  185. package/dist/integrity/slopnators/sl_unversioned.ts +5 -3
  186. package/dist/integrity/sweep.d.ts +1 -0
  187. package/dist/integrity/sweep.js +105 -42
  188. package/dist/integrity/sweep.ts +115 -42
  189. package/dist/knowledge/attach.js +4 -3
  190. package/dist/knowledge/attach.ts +3 -2
  191. package/dist/knowledge/generators/docsgen.js +8 -6
  192. package/dist/knowledge/generators/docsgen.ts +4 -2
  193. package/dist/knowledge/generators/glossary.js +4 -4
  194. package/dist/knowledge/generators/glossary.ts +1 -1
  195. package/dist/operations/access.d.ts +23 -0
  196. package/dist/operations/access.js +28 -1
  197. package/dist/operations/access.ts +43 -0
  198. package/dist/operations/attach.js +33 -113
  199. package/dist/operations/attach.ts +51 -117
  200. package/dist/operations/capabilities.d.ts +14 -5
  201. package/dist/operations/capabilities.js +13 -4
  202. package/dist/operations/capabilities.ts +27 -8
  203. package/dist/operations/dispatch.d.ts +8 -2
  204. package/dist/operations/dispatch.js +128 -185
  205. package/dist/operations/dispatch.ts +138 -191
  206. package/dist/operations/error-results.d.ts +1 -1
  207. package/dist/operations/error-results.js +2 -2
  208. package/dist/operations/error-results.ts +2 -2
  209. package/dist/operations/generators/capability.d.ts +11 -1
  210. package/dist/operations/generators/capability.js +481 -224
  211. package/dist/operations/generators/capability.ts +562 -233
  212. package/dist/operations/generators/compile.js +66 -39
  213. package/dist/operations/generators/compile.ts +78 -39
  214. package/dist/operations/generators/permits.js +11 -12
  215. package/dist/operations/generators/permits.ts +3 -4
  216. package/dist/operations/generators/seed.d.ts +1 -0
  217. package/dist/operations/generators/seed.js +19 -17
  218. package/dist/operations/generators/seed.ts +22 -14
  219. package/dist/operations/generators/trial.d.ts +6 -8
  220. package/dist/operations/generators/trial.js +200 -70
  221. package/dist/operations/generators/trial.ts +230 -93
  222. package/dist/operations/nodes.d.ts +15 -16
  223. package/dist/operations/nodes.ts +16 -17
  224. package/dist/operations/operations.d.ts +40 -4
  225. package/dist/operations/operations.js +1 -0
  226. package/dist/operations/operations.ts +61 -4
  227. package/dist/operations/result.d.ts +2 -2
  228. package/dist/operations/result.ts +2 -2
  229. package/package.json +1 -4
  230. package/dist/core/answers.d.ts +0 -171
  231. package/dist/core/answers.js +0 -789
  232. package/dist/core/answers.ts +0 -999
  233. package/dist/core/client.d.ts +0 -63
  234. package/dist/core/client.js +0 -249
  235. package/dist/core/client.ts +0 -289
  236. package/dist/core/files.js +0 -171
  237. package/dist/core/files.ts +0 -222
  238. package/dist/integrations/module.d.ts +0 -6
  239. package/dist/integrations/module.js +0 -153
  240. package/dist/integrations/module.ts +0 -191
  241. package/dist/integrity/slopnators/sl_unkept_rows.js +0 -104
  242. package/dist/integrity/slopnators/sl_unkept_rows.ts +0 -111
  243. package/dist/integrity/slopnators/sl_unseamed_reach.js +0 -73
  244. package/dist/integrity/slopnators/sl_unseamed_reach.ts +0 -79
  245. package/dist/operations/cost.d.ts +0 -14
  246. package/dist/operations/cost.js +0 -37
  247. package/dist/operations/cost.ts +0 -58
  248. package/dist/operations/errors/errors.d.ts +0 -52
  249. package/dist/operations/http.js +0 -124
  250. package/dist/operations/http.ts +0 -169
  251. package/dist/operations/memory.d.ts +0 -104
  252. package/dist/operations/memory.js +0 -365
  253. package/dist/operations/memory.ts +0 -470
  254. package/dist/operations/metering.d.ts +0 -55
  255. package/dist/operations/metering.js +0 -247
  256. package/dist/operations/metering.ts +0 -328
  257. package/dist/operations/queue-policy.d.ts +0 -3
  258. package/dist/operations/queue-policy.js +0 -8
  259. package/dist/operations/queue-policy.ts +0 -10
  260. package/dist/operations/seams.d.ts +0 -239
  261. package/dist/operations/seams.js +0 -45
  262. package/dist/operations/seams.ts +0 -364
  263. package/dist/operations/store.d.ts +0 -14
  264. package/dist/operations/store.js +0 -1
  265. package/dist/operations/store.ts +0 -29
  266. /package/dist/{operations → core}/errors/classes.d.ts +0 -0
  267. /package/dist/{operations → core}/errors/classes.js +0 -0
  268. /package/dist/{operations → core}/errors/classes.ts +0 -0
  269. /package/dist/integrity/slopnators/{sl_unkept_rows.d.ts → sl_door_in_a_node.d.ts} +0 -0
  270. /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,3 +0,0 @@
1
- export declare const MAX_ATTEMPTS = 5;
2
- export declare function backoffMs(attempts: number): number;
3
- export declare function isExhausted(attempts: number): boolean;
@@ -1,8 +0,0 @@
1
- export const MAX_ATTEMPTS = 5;
2
- export function backoffMs(attempts) {
3
- const base = 1_000 * 2 ** Math.max(0, attempts - 1);
4
- return Math.min(base, 5 * 60_000);
5
- }
6
- export function isExhausted(attempts) {
7
- return attempts >= MAX_ATTEMPTS;
8
- }
@@ -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
- }