camusdb 0.1.0

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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +950 -0
  3. package/dist/auth/credentials.d.ts +30 -0
  4. package/dist/auth/credentials.d.ts.map +1 -0
  5. package/dist/auth/credentials.js +23 -0
  6. package/dist/auth/credentials.js.map +1 -0
  7. package/dist/auth/login-client.d.ts +37 -0
  8. package/dist/auth/login-client.d.ts.map +1 -0
  9. package/dist/auth/login-client.js +8 -0
  10. package/dist/auth/login-client.js.map +1 -0
  11. package/dist/auth/rest-login-client.d.ts +17 -0
  12. package/dist/auth/rest-login-client.d.ts.map +1 -0
  13. package/dist/auth/rest-login-client.js +71 -0
  14. package/dist/auth/rest-login-client.js.map +1 -0
  15. package/dist/auth/token-provider.d.ts +123 -0
  16. package/dist/auth/token-provider.d.ts.map +1 -0
  17. package/dist/auth/token-provider.js +209 -0
  18. package/dist/auth/token-provider.js.map +1 -0
  19. package/dist/backup.d.ts +98 -0
  20. package/dist/backup.d.ts.map +1 -0
  21. package/dist/backup.js +153 -0
  22. package/dist/backup.js.map +1 -0
  23. package/dist/cache.d.ts +90 -0
  24. package/dist/cache.d.ts.map +1 -0
  25. package/dist/cache.js +121 -0
  26. package/dist/cache.js.map +1 -0
  27. package/dist/client.d.ts +406 -0
  28. package/dist/client.d.ts.map +1 -0
  29. package/dist/client.js +724 -0
  30. package/dist/client.js.map +1 -0
  31. package/dist/column-type.d.ts +33 -0
  32. package/dist/column-type.d.ts.map +1 -0
  33. package/dist/column-type.js +50 -0
  34. package/dist/column-type.js.map +1 -0
  35. package/dist/column-value.d.ts +77 -0
  36. package/dist/column-value.d.ts.map +1 -0
  37. package/dist/column-value.js +60 -0
  38. package/dist/column-value.js.map +1 -0
  39. package/dist/config.d.ts +181 -0
  40. package/dist/config.d.ts.map +1 -0
  41. package/dist/config.js +315 -0
  42. package/dist/config.js.map +1 -0
  43. package/dist/connection-string.d.ts +45 -0
  44. package/dist/connection-string.d.ts.map +1 -0
  45. package/dist/connection-string.js +170 -0
  46. package/dist/connection-string.js.map +1 -0
  47. package/dist/endpoint-pool.d.ts +70 -0
  48. package/dist/endpoint-pool.d.ts.map +1 -0
  49. package/dist/endpoint-pool.js +120 -0
  50. package/dist/endpoint-pool.js.map +1 -0
  51. package/dist/error-codes.d.ts +44 -0
  52. package/dist/error-codes.d.ts.map +1 -0
  53. package/dist/error-codes.js +43 -0
  54. package/dist/error-codes.js.map +1 -0
  55. package/dist/errors.d.ts +24 -0
  56. package/dist/errors.d.ts.map +1 -0
  57. package/dist/errors.js +33 -0
  58. package/dist/errors.js.map +1 -0
  59. package/dist/hlc.d.ts +19 -0
  60. package/dist/hlc.d.ts.map +1 -0
  61. package/dist/hlc.js +11 -0
  62. package/dist/hlc.js.map +1 -0
  63. package/dist/index.d.ts +55 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +51 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/json.d.ts +41 -0
  68. package/dist/json.d.ts.map +1 -0
  69. package/dist/json.js +345 -0
  70. package/dist/json.js.map +1 -0
  71. package/dist/object-id.d.ts +39 -0
  72. package/dist/object-id.d.ts.map +1 -0
  73. package/dist/object-id.js +99 -0
  74. package/dist/object-id.js.map +1 -0
  75. package/dist/options.d.ts +77 -0
  76. package/dist/options.d.ts.map +1 -0
  77. package/dist/options.js +68 -0
  78. package/dist/options.js.map +1 -0
  79. package/dist/prepared/binder.d.ts +20 -0
  80. package/dist/prepared/binder.d.ts.map +1 -0
  81. package/dist/prepared/binder.js +32 -0
  82. package/dist/prepared/binder.js.map +1 -0
  83. package/dist/prepared/policy.d.ts +81 -0
  84. package/dist/prepared/policy.d.ts.map +1 -0
  85. package/dist/prepared/policy.js +164 -0
  86. package/dist/prepared/policy.js.map +1 -0
  87. package/dist/query-stream.d.ts +55 -0
  88. package/dist/query-stream.d.ts.map +1 -0
  89. package/dist/query-stream.js +94 -0
  90. package/dist/query-stream.js.map +1 -0
  91. package/dist/result-set.d.ts +62 -0
  92. package/dist/result-set.d.ts.map +1 -0
  93. package/dist/result-set.js +100 -0
  94. package/dist/result-set.js.map +1 -0
  95. package/dist/retry.d.ts +38 -0
  96. package/dist/retry.d.ts.map +1 -0
  97. package/dist/retry.js +93 -0
  98. package/dist/retry.js.map +1 -0
  99. package/dist/routing/advice.d.ts +63 -0
  100. package/dist/routing/advice.d.ts.map +1 -0
  101. package/dist/routing/advice.js +61 -0
  102. package/dist/routing/advice.js.map +1 -0
  103. package/dist/routing/route-cache.d.ts +53 -0
  104. package/dist/routing/route-cache.d.ts.map +1 -0
  105. package/dist/routing/route-cache.js +123 -0
  106. package/dist/routing/route-cache.js.map +1 -0
  107. package/dist/routing/router.d.ts +53 -0
  108. package/dist/routing/router.d.ts.map +1 -0
  109. package/dist/routing/router.js +89 -0
  110. package/dist/routing/router.js.map +1 -0
  111. package/dist/row-source.d.ts +48 -0
  112. package/dist/row-source.d.ts.map +1 -0
  113. package/dist/row-source.js +35 -0
  114. package/dist/row-source.js.map +1 -0
  115. package/dist/runtime.d.ts +58 -0
  116. package/dist/runtime.d.ts.map +1 -0
  117. package/dist/runtime.js +137 -0
  118. package/dist/runtime.js.map +1 -0
  119. package/dist/shared-registry.d.ts +32 -0
  120. package/dist/shared-registry.d.ts.map +1 -0
  121. package/dist/shared-registry.js +48 -0
  122. package/dist/shared-registry.js.map +1 -0
  123. package/dist/sql-syntax.d.ts +44 -0
  124. package/dist/sql-syntax.d.ts.map +1 -0
  125. package/dist/sql-syntax.js +83 -0
  126. package/dist/sql-syntax.js.map +1 -0
  127. package/dist/statements.d.ts +15 -0
  128. package/dist/statements.d.ts.map +1 -0
  129. package/dist/statements.js +90 -0
  130. package/dist/statements.js.map +1 -0
  131. package/dist/transaction.d.ts +117 -0
  132. package/dist/transaction.d.ts.map +1 -0
  133. package/dist/transaction.js +204 -0
  134. package/dist/transaction.js.map +1 -0
  135. package/dist/transport/authenticating-transport.d.ts +63 -0
  136. package/dist/transport/authenticating-transport.d.ts.map +1 -0
  137. package/dist/transport/authenticating-transport.js +119 -0
  138. package/dist/transport/authenticating-transport.js.map +1 -0
  139. package/dist/transport/branch-statements.d.ts +39 -0
  140. package/dist/transport/branch-statements.d.ts.map +1 -0
  141. package/dist/transport/branch-statements.js +74 -0
  142. package/dist/transport/branch-statements.js.map +1 -0
  143. package/dist/transport/error-text.d.ts +26 -0
  144. package/dist/transport/error-text.d.ts.map +1 -0
  145. package/dist/transport/error-text.js +75 -0
  146. package/dist/transport/error-text.js.map +1 -0
  147. package/dist/transport/grpc/batcher.d.ts +127 -0
  148. package/dist/transport/grpc/batcher.d.ts.map +1 -0
  149. package/dist/transport/grpc/batcher.js +399 -0
  150. package/dist/transport/grpc/batcher.js.map +1 -0
  151. package/dist/transport/grpc/codec.d.ts +40 -0
  152. package/dist/transport/grpc/codec.d.ts.map +1 -0
  153. package/dist/transport/grpc/codec.js +231 -0
  154. package/dist/transport/grpc/codec.js.map +1 -0
  155. package/dist/transport/grpc/grpc-transport.d.ts +115 -0
  156. package/dist/transport/grpc/grpc-transport.d.ts.map +1 -0
  157. package/dist/transport/grpc/grpc-transport.js +577 -0
  158. package/dist/transport/grpc/grpc-transport.js.map +1 -0
  159. package/dist/transport/grpc/messages.d.ts +221 -0
  160. package/dist/transport/grpc/messages.d.ts.map +1 -0
  161. package/dist/transport/grpc/messages.js +56 -0
  162. package/dist/transport/grpc/messages.js.map +1 -0
  163. package/dist/transport/grpc/proto.d.ts +71 -0
  164. package/dist/transport/grpc/proto.d.ts.map +1 -0
  165. package/dist/transport/grpc/proto.js +67 -0
  166. package/dist/transport/grpc/proto.js.map +1 -0
  167. package/dist/transport/http.d.ts +62 -0
  168. package/dist/transport/http.d.ts.map +1 -0
  169. package/dist/transport/http.js +169 -0
  170. package/dist/transport/http.js.map +1 -0
  171. package/dist/transport/ndjson-row-source.d.ts +38 -0
  172. package/dist/transport/ndjson-row-source.d.ts.map +1 -0
  173. package/dist/transport/ndjson-row-source.js +197 -0
  174. package/dist/transport/ndjson-row-source.js.map +1 -0
  175. package/dist/transport/rest-decode.d.ts +33 -0
  176. package/dist/transport/rest-decode.d.ts.map +1 -0
  177. package/dist/transport/rest-decode.js +162 -0
  178. package/dist/transport/rest-decode.js.map +1 -0
  179. package/dist/transport/rest-prepared-cache.d.ts +50 -0
  180. package/dist/transport/rest-prepared-cache.d.ts.map +1 -0
  181. package/dist/transport/rest-prepared-cache.js +101 -0
  182. package/dist/transport/rest-prepared-cache.js.map +1 -0
  183. package/dist/transport/rest-transport.d.ts +88 -0
  184. package/dist/transport/rest-transport.d.ts.map +1 -0
  185. package/dist/transport/rest-transport.js +388 -0
  186. package/dist/transport/rest-transport.js.map +1 -0
  187. package/dist/transport/transport-pool.d.ts +40 -0
  188. package/dist/transport/transport-pool.d.ts.map +1 -0
  189. package/dist/transport/transport-pool.js +48 -0
  190. package/dist/transport/transport-pool.js.map +1 -0
  191. package/dist/transport/transport.d.ts +199 -0
  192. package/dist/transport/transport.d.ts.map +1 -0
  193. package/dist/transport/transport.js +11 -0
  194. package/dist/transport/transport.js.map +1 -0
  195. package/dist/values/decode.d.ts +39 -0
  196. package/dist/values/decode.d.ts.map +1 -0
  197. package/dist/values/decode.js +75 -0
  198. package/dist/values/decode.js.map +1 -0
  199. package/dist/values/encode.d.ts +45 -0
  200. package/dist/values/encode.d.ts.map +1 -0
  201. package/dist/values/encode.js +271 -0
  202. package/dist/values/encode.js.map +1 -0
  203. package/dist/values/ticks.d.ts +31 -0
  204. package/dist/values/ticks.d.ts.map +1 -0
  205. package/dist/values/ticks.js +52 -0
  206. package/dist/values/ticks.js.map +1 -0
  207. package/dist/values/typed.d.ts +67 -0
  208. package/dist/values/typed.d.ts.map +1 -0
  209. package/dist/values/typed.js +134 -0
  210. package/dist/values/typed.js.map +1 -0
  211. package/dist/values/uuid.d.ts +27 -0
  212. package/dist/values/uuid.d.ts.map +1 -0
  213. package/dist/values/uuid.js +76 -0
  214. package/dist/values/uuid.js.map +1 -0
  215. package/dist/vector.d.ts +26 -0
  216. package/dist/vector.d.ts.map +1 -0
  217. package/dist/vector.js +48 -0
  218. package/dist/vector.js.map +1 -0
  219. package/package.json +86 -0
  220. package/proto/camus_sql.proto +566 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import type { CamusTransactionOptions } from './options.js';
8
+ import type { ClientRuntime } from './runtime.js';
9
+ /**
10
+ * A set of reads and writes that take effect atomically, at one logical instant, across columns,
11
+ * rows, and tables.
12
+ *
13
+ * **Where a transaction starts.** With learned routing off, `BEGIN` is sent when the transaction is
14
+ * created, on the pool's next endpoint. With learned routing on, `BEGIN` is deferred: the
15
+ * transaction is started by its first statement, on the endpoint that statement's learned route
16
+ * names, so a transaction whose first statement touches a table runs where that table's leader is.
17
+ * Once started it is pinned to that endpoint for its whole life; a statement's own route may inform
18
+ * a later transaction, but never relocates this one. A caller that knows the transaction's hot
19
+ * statement can choose the endpoint at `BEGIN` with `options.affinity` instead.
20
+ *
21
+ * Two consequences of the deferral are visible, and only in routed mode: the server-minted identity
22
+ * reads as zero until the first statement, and a failure to begin surfaces from that first
23
+ * statement rather than from the call that created the transaction.
24
+ */
25
+ export declare class CamusTransaction {
26
+ private readonly runtime;
27
+ private txnIdPTValue;
28
+ private txnIdCounterValue;
29
+ private endpointValue;
30
+ private startedValue;
31
+ /**
32
+ * The single `BEGIN` in flight or finished, for a deferred transaction. Every racing first
33
+ * statement awaits this same promise, and a `BEGIN` that failed stays failed: nothing was minted,
34
+ * the transaction is unusable, and the caller begins a new one — which is the state a failed
35
+ * eager `BEGIN` leaves too.
36
+ */
37
+ private startPromise;
38
+ private startFailed;
39
+ private finalized;
40
+ /** The concurrency knobs this transaction was begun with, after every default was applied. */
41
+ readonly options: CamusTransactionOptions;
42
+ /**
43
+ * The stream slot this transaction's statements pin to, so the server sees them in one ordering
44
+ * chain. It is a gRPC concept; REST does not pin.
45
+ */
46
+ streamSlot: number | undefined;
47
+ /** @internal Use `CamusClient.beginTransaction`. */
48
+ constructor(runtime: ClientRuntime, options: CamusTransactionOptions, started?: {
49
+ txnIdPT: bigint;
50
+ txnIdCounter: number;
51
+ endpoint: string;
52
+ streamSlot?: number | undefined;
53
+ });
54
+ /**
55
+ * True once the server minted this transaction.
56
+ *
57
+ * It is always true with learned routing off. With routing on it is false until the first
58
+ * statement, or until an explicit `affinity`, starts it.
59
+ */
60
+ get isStarted(): boolean;
61
+ /** True once this transaction was committed or rolled back. */
62
+ get isFinalized(): boolean;
63
+ /** The physical-time half of the server-minted identity. Zero until the transaction starts. */
64
+ get txnIdPT(): bigint;
65
+ /** The counter half of the server-minted identity. Zero until the transaction starts. */
66
+ get txnIdCounter(): number;
67
+ /** The identity as one string, for logs and correlation. */
68
+ get transactionId(): string;
69
+ /** The endpoint this transaction is pinned to, or `undefined` while it has not started. */
70
+ get endpoint(): string | undefined;
71
+ /**
72
+ * Starts the transaction if it has not started, and reports the endpoint it is pinned to.
73
+ *
74
+ * It starts on `preferredEndpoint` when one is given — a learned route for the statement about
75
+ * to run — and on the pool's rotation otherwise. The first caller sends `BEGIN`; a concurrent
76
+ * caller awaits that same send. Once started, a later call's preferred endpoint is ignored,
77
+ * because the pin is final.
78
+ *
79
+ * @internal
80
+ */
81
+ ensureStarted(preferredEndpoint: string | undefined, signal?: AbortSignal): Promise<string>;
82
+ private startOn;
83
+ /** Makes this transaction's writes durable. */
84
+ commit(signal?: AbortSignal): Promise<void>;
85
+ /** Discards this transaction's writes. */
86
+ rollback(signal?: AbortSignal): Promise<void>;
87
+ /**
88
+ * Issues a commit or a rollback, and resolves a `CADB0509` outcome by re-issuing the **same**
89
+ * finalize on the **same** handle, bounded and backing off.
90
+ *
91
+ * `CADB0509` says the outcome is not known yet. The transaction is not dead, so the operation
92
+ * must never be replayed from `BEGIN`: that could apply an already-durable commit a second time.
93
+ * Every other failure reaches the caller.
94
+ *
95
+ * A deferred transaction that ran no statement is started here first, on the pool's rotation, so
96
+ * the server sees the same `BEGIN` and finalize pair it always did.
97
+ */
98
+ private finalize;
99
+ /**
100
+ * Rolls the transaction back if it was neither committed nor rolled back, so a `using` block
101
+ * cannot leave one open. A rollback that itself fails is swallowed: the block is unwinding, and
102
+ * the server ends an abandoned transaction on its own session timeout.
103
+ */
104
+ [Symbol.asyncDispose](): Promise<void>;
105
+ /**
106
+ * The routing kind a statement of this transaction reports. Statements inside a transaction still
107
+ * negotiate and learn, for the benefit of the next transaction, but the route they learn never
108
+ * moves this one.
109
+ *
110
+ * @internal
111
+ */
112
+ static readonly routeKind: {
113
+ readonly Query: 0;
114
+ readonly NonQuery: 1;
115
+ };
116
+ }
117
+ //# sourceMappingURL=transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,OAAO,CAAC,YAAY,CAAM;IAE1B,OAAO,CAAC,iBAAiB,CAAK;IAE9B,OAAO,CAAC,aAAa,CAAqB;IAE1C,OAAO,CAAC,YAAY,CAAS;IAE7B;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAA4B;IAEhD,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,SAAS,CAAS;IAE1B,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;IAE1C;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/B,oDAAoD;gBAElD,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,uBAAuB,EAChC,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAcxG;;;;;OAKG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,+DAA+D;IAC/D,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,+FAA+F;IAC/F,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,yFAAyF;IACzF,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,4DAA4D;IAC5D,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,2FAA2F;IAC3F,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED;;;;;;;;;OASG;IACG,aAAa,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;YAUnF,OAAO;IAqBrB,+CAA+C;IAC/C,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C;;;;;;;;;;OAUG;YACW,QAAQ;IA4CtB;;;;OAIG;IACG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5C;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS;;;MAAoB;CAC9C"}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import { CamusError } from './errors.js';
8
+ import { CamusErrorCode } from './error-codes.js';
9
+ import { delay } from './retry.js';
10
+ import { CamusRouteOpKind } from './routing/route-cache.js';
11
+ /**
12
+ * A set of reads and writes that take effect atomically, at one logical instant, across columns,
13
+ * rows, and tables.
14
+ *
15
+ * **Where a transaction starts.** With learned routing off, `BEGIN` is sent when the transaction is
16
+ * created, on the pool's next endpoint. With learned routing on, `BEGIN` is deferred: the
17
+ * transaction is started by its first statement, on the endpoint that statement's learned route
18
+ * names, so a transaction whose first statement touches a table runs where that table's leader is.
19
+ * Once started it is pinned to that endpoint for its whole life; a statement's own route may inform
20
+ * a later transaction, but never relocates this one. A caller that knows the transaction's hot
21
+ * statement can choose the endpoint at `BEGIN` with `options.affinity` instead.
22
+ *
23
+ * Two consequences of the deferral are visible, and only in routed mode: the server-minted identity
24
+ * reads as zero until the first statement, and a failure to begin surfaces from that first
25
+ * statement rather than from the call that created the transaction.
26
+ */
27
+ export class CamusTransaction {
28
+ runtime;
29
+ txnIdPTValue = 0n;
30
+ txnIdCounterValue = 0;
31
+ endpointValue;
32
+ startedValue = false;
33
+ /**
34
+ * The single `BEGIN` in flight or finished, for a deferred transaction. Every racing first
35
+ * statement awaits this same promise, and a `BEGIN` that failed stays failed: nothing was minted,
36
+ * the transaction is unusable, and the caller begins a new one — which is the state a failed
37
+ * eager `BEGIN` leaves too.
38
+ */
39
+ startPromise;
40
+ startFailed = false;
41
+ finalized = false;
42
+ /** The concurrency knobs this transaction was begun with, after every default was applied. */
43
+ options;
44
+ /**
45
+ * The stream slot this transaction's statements pin to, so the server sees them in one ordering
46
+ * chain. It is a gRPC concept; REST does not pin.
47
+ */
48
+ streamSlot;
49
+ /** @internal Use `CamusClient.beginTransaction`. */
50
+ constructor(runtime, options, started) {
51
+ this.runtime = runtime;
52
+ this.options = options;
53
+ if (started !== undefined) {
54
+ this.txnIdPTValue = started.txnIdPT;
55
+ this.txnIdCounterValue = started.txnIdCounter;
56
+ this.endpointValue = started.endpoint;
57
+ this.streamSlot = started.streamSlot;
58
+ this.startedValue = true;
59
+ }
60
+ }
61
+ /**
62
+ * True once the server minted this transaction.
63
+ *
64
+ * It is always true with learned routing off. With routing on it is false until the first
65
+ * statement, or until an explicit `affinity`, starts it.
66
+ */
67
+ get isStarted() {
68
+ return this.startedValue;
69
+ }
70
+ /** True once this transaction was committed or rolled back. */
71
+ get isFinalized() {
72
+ return this.finalized;
73
+ }
74
+ /** The physical-time half of the server-minted identity. Zero until the transaction starts. */
75
+ get txnIdPT() {
76
+ return this.txnIdPTValue;
77
+ }
78
+ /** The counter half of the server-minted identity. Zero until the transaction starts. */
79
+ get txnIdCounter() {
80
+ return this.txnIdCounterValue;
81
+ }
82
+ /** The identity as one string, for logs and correlation. */
83
+ get transactionId() {
84
+ return `${this.txnIdPTValue.toString()}:${String(this.txnIdCounterValue)}`;
85
+ }
86
+ /** The endpoint this transaction is pinned to, or `undefined` while it has not started. */
87
+ get endpoint() {
88
+ return this.endpointValue;
89
+ }
90
+ /**
91
+ * Starts the transaction if it has not started, and reports the endpoint it is pinned to.
92
+ *
93
+ * It starts on `preferredEndpoint` when one is given — a learned route for the statement about
94
+ * to run — and on the pool's rotation otherwise. The first caller sends `BEGIN`; a concurrent
95
+ * caller awaits that same send. Once started, a later call's preferred endpoint is ignored,
96
+ * because the pin is final.
97
+ *
98
+ * @internal
99
+ */
100
+ async ensureStarted(preferredEndpoint, signal) {
101
+ if (!this.startedValue) {
102
+ this.startPromise ??= this.startOn(preferredEndpoint ?? this.runtime.nextEndpoint(), signal);
103
+ await this.startPromise;
104
+ }
105
+ return this.endpointValue;
106
+ }
107
+ async startOn(target, signal) {
108
+ try {
109
+ const result = await this.runtime.transport.startTransaction(target, this.runtime.database, this.options, this.runtime.timeoutSeconds, signal);
110
+ this.txnIdPTValue = result.txnIdPT;
111
+ this.txnIdCounterValue = result.txnIdCounter;
112
+ this.streamSlot = result.streamSlot;
113
+ this.endpointValue = target;
114
+ this.startedValue = true;
115
+ }
116
+ catch (error) {
117
+ this.startFailed = true;
118
+ throw error;
119
+ }
120
+ }
121
+ /** Makes this transaction's writes durable. */
122
+ commit(signal) {
123
+ return this.finalize(true, signal);
124
+ }
125
+ /** Discards this transaction's writes. */
126
+ rollback(signal) {
127
+ return this.finalize(false, signal);
128
+ }
129
+ /**
130
+ * Issues a commit or a rollback, and resolves a `CADB0509` outcome by re-issuing the **same**
131
+ * finalize on the **same** handle, bounded and backing off.
132
+ *
133
+ * `CADB0509` says the outcome is not known yet. The transaction is not dead, so the operation
134
+ * must never be replayed from `BEGIN`: that could apply an already-durable commit a second time.
135
+ * Every other failure reaches the caller.
136
+ *
137
+ * A deferred transaction that ran no statement is started here first, on the pool's rotation, so
138
+ * the server sees the same `BEGIN` and finalize pair it always did.
139
+ */
140
+ async finalize(commit, signal) {
141
+ if (this.finalized) {
142
+ throw new CamusError(CamusErrorCode.Generic, 'This transaction was already committed or rolled back.');
143
+ }
144
+ // A deferred `BEGIN` that failed left nothing on the server. The failure already surfaced at
145
+ // the statement that triggered it, so a rollback — which a scope issues without looking — has
146
+ // nothing to undo and finishes quietly, while a commit still reports the failed start.
147
+ if (!commit && !this.startedValue && this.startFailed) {
148
+ this.finalized = true;
149
+ return;
150
+ }
151
+ const target = await this.ensureStarted(undefined, signal);
152
+ for (let attempt = 0;; attempt++) {
153
+ try {
154
+ await this.runtime.transport.finalizeTransaction(commit, target, this.runtime.database, this.txnIdPTValue, this.txnIdCounterValue, this.streamSlot, this.runtime.timeoutSeconds, signal);
155
+ this.finalized = true;
156
+ return;
157
+ }
158
+ catch (error) {
159
+ if (!CamusError.is(error) ||
160
+ error.code !== CamusErrorCode.FinalizeUnresolved ||
161
+ attempt >= MAX_FINALIZE_ATTEMPTS) {
162
+ throw error;
163
+ }
164
+ await delay(finalizeDelayMs(attempt), signal);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Rolls the transaction back if it was neither committed nor rolled back, so a `using` block
170
+ * cannot leave one open. A rollback that itself fails is swallowed: the block is unwinding, and
171
+ * the server ends an abandoned transaction on its own session timeout.
172
+ */
173
+ async [Symbol.asyncDispose]() {
174
+ if (this.finalized)
175
+ return;
176
+ try {
177
+ await this.rollback();
178
+ }
179
+ catch {
180
+ // Nothing useful can be done while a scope is unwinding.
181
+ }
182
+ }
183
+ /**
184
+ * The routing kind a statement of this transaction reports. Statements inside a transaction still
185
+ * negotiate and learn, for the benefit of the next transaction, but the route they learn never
186
+ * moves this one.
187
+ *
188
+ * @internal
189
+ */
190
+ static routeKind = CamusRouteOpKind;
191
+ }
192
+ /**
193
+ * How many times an unresolved commit or rollback is re-issued on the same handle before the error
194
+ * reaches the caller. The server's own session timeout is the ultimate backstop.
195
+ */
196
+ const MAX_FINALIZE_ATTEMPTS = 10;
197
+ /**
198
+ * The finalize back-off: 50 ms doubling to about 3.2 seconds, which matches the server's contract
199
+ * for an unresolved outcome.
200
+ */
201
+ function finalizeDelayMs(attempt) {
202
+ return 50 * 2 ** Math.min(attempt, 6);
203
+ }
204
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import type { CamusTokenProvider } from '../auth/token-provider.js';
8
+ import type { CamusProtocol } from '../config.js';
9
+ import type { CamusTransactionOptions } from '../options.js';
10
+ import type { CamusRowSource } from '../row-source.js';
11
+ import type { CamusBranchRow, CamusTransport, NonQueryTransportResult, PreparedStatementInfo, QueryTransportResult, StartTransactionResult, TransportInsertRequest, TransportSqlRequest } from './transport.js';
12
+ /**
13
+ * Wraps a transport with the reactive half of token management.
14
+ *
15
+ * The inner transport attaches whatever token the provider currently holds. This wrapper watches
16
+ * for the server rejecting it with `CADB0516`, discards it, and replays the operation once with a
17
+ * freshly minted one.
18
+ *
19
+ * That covers the failures a client-side expiry timer cannot see: a rotated password, a dropped
20
+ * user, a logout from elsewhere, a server configured with a shorter token lifetime than the driver
21
+ * assumes, or a token minted before a server restart. It does **not** retry `CADB0517`,
22
+ * insufficient privilege: authenticating again as the same user cannot grant a privilege, so a
23
+ * retry would only double the work before the same refusal.
24
+ *
25
+ * There is exactly one replay per call, and only when the attempt actually presented a token the
26
+ * provider can replace. A rejection with no token in hand came from the login itself — a wrong
27
+ * password — and replaying it would burn two attempts per statement against a limit of twenty per
28
+ * account per minute. A token supplied directly is likewise reported as it is, because there is no
29
+ * password to mint a replacement with.
30
+ *
31
+ * A replayed statement never reached execution, so a replay here is no less safe than the retry
32
+ * paths the driver already runs for a serializable conflict.
33
+ */
34
+ export declare class AuthenticatingTransport implements CamusTransport {
35
+ /** The wrapped transport, for tests and for the login client the gRPC transport also is. */
36
+ readonly inner: CamusTransport;
37
+ private readonly auth;
38
+ constructor(inner: CamusTransport, auth: CamusTokenProvider);
39
+ get protocol(): CamusProtocol;
40
+ startTransaction(endpoint: string, database: string, options: CamusTransactionOptions, timeoutSeconds: number, signal?: AbortSignal): Promise<StartTransactionResult>;
41
+ finalizeTransaction(commit: boolean, endpoint: string, database: string, txnIdPT: bigint, txnIdCounter: number, streamSlot: number | undefined, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
42
+ executeQuery(request: TransportSqlRequest): Promise<QueryTransportResult>;
43
+ executeQueryStream(request: TransportSqlRequest): Promise<CamusRowSource>;
44
+ executeNonQuery(request: TransportSqlRequest): Promise<NonQueryTransportResult>;
45
+ insert(request: TransportInsertRequest): Promise<number>;
46
+ executeDdl(request: TransportSqlRequest): Promise<boolean>;
47
+ prepare(endpoint: string, database: string, sql: string, timeoutSeconds: number, signal?: AbortSignal): Promise<PreparedStatementInfo>;
48
+ /**
49
+ * Closing is best-effort and idempotent. A token the server has already rejected means the
50
+ * handle it named is unreachable anyway, so there is nothing a replay could still release.
51
+ */
52
+ closePrepared(endpoint: string, database: string, sql: string, signal?: AbortSignal): Promise<void>;
53
+ ping(endpoint: string, timeoutSeconds: number, signal?: AbortSignal): Promise<boolean>;
54
+ createDatabase(endpoint: string, database: string, ifNotExists: boolean, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
55
+ createBranchDatabase(endpoint: string, branchName: string, sourceDatabaseName: string, ifNotExists: boolean, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
56
+ dropDatabase(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
57
+ showBranches(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusBranchRow[]>;
58
+ showAncestors(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusBranchRow[]>;
59
+ close(): Promise<void>;
60
+ private run;
61
+ private isRenewable;
62
+ }
63
+ //# sourceMappingURL=authenticating-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticating-transport.d.ts","sourceRoot":"","sources":["../../src/transport/authenticating-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import { CamusError } from '../errors.js';
8
+ import { CamusErrorCode } from '../error-codes.js';
9
+ /**
10
+ * Wraps a transport with the reactive half of token management.
11
+ *
12
+ * The inner transport attaches whatever token the provider currently holds. This wrapper watches
13
+ * for the server rejecting it with `CADB0516`, discards it, and replays the operation once with a
14
+ * freshly minted one.
15
+ *
16
+ * That covers the failures a client-side expiry timer cannot see: a rotated password, a dropped
17
+ * user, a logout from elsewhere, a server configured with a shorter token lifetime than the driver
18
+ * assumes, or a token minted before a server restart. It does **not** retry `CADB0517`,
19
+ * insufficient privilege: authenticating again as the same user cannot grant a privilege, so a
20
+ * retry would only double the work before the same refusal.
21
+ *
22
+ * There is exactly one replay per call, and only when the attempt actually presented a token the
23
+ * provider can replace. A rejection with no token in hand came from the login itself — a wrong
24
+ * password — and replaying it would burn two attempts per statement against a limit of twenty per
25
+ * account per minute. A token supplied directly is likewise reported as it is, because there is no
26
+ * password to mint a replacement with.
27
+ *
28
+ * A replayed statement never reached execution, so a replay here is no less safe than the retry
29
+ * paths the driver already runs for a serializable conflict.
30
+ */
31
+ export class AuthenticatingTransport {
32
+ /** The wrapped transport, for tests and for the login client the gRPC transport also is. */
33
+ inner;
34
+ auth;
35
+ constructor(inner, auth) {
36
+ this.inner = inner;
37
+ this.auth = auth;
38
+ }
39
+ get protocol() {
40
+ return this.inner.protocol;
41
+ }
42
+ startTransaction(endpoint, database, options, timeoutSeconds, signal) {
43
+ return this.run(() => this.inner.startTransaction(endpoint, database, options, timeoutSeconds, signal));
44
+ }
45
+ finalizeTransaction(commit, endpoint, database, txnIdPT, txnIdCounter, streamSlot, timeoutSeconds, signal) {
46
+ return this.run(() => this.inner.finalizeTransaction(commit, endpoint, database, txnIdPT, txnIdCounter, streamSlot, timeoutSeconds, signal));
47
+ }
48
+ executeQuery(request) {
49
+ return this.run(() => this.inner.executeQuery(request));
50
+ }
51
+ executeQueryStream(request) {
52
+ return this.run(() => this.inner.executeQueryStream(request));
53
+ }
54
+ executeNonQuery(request) {
55
+ return this.run(() => this.inner.executeNonQuery(request));
56
+ }
57
+ insert(request) {
58
+ return this.run(() => this.inner.insert(request));
59
+ }
60
+ executeDdl(request) {
61
+ return this.run(() => this.inner.executeDdl(request));
62
+ }
63
+ prepare(endpoint, database, sql, timeoutSeconds, signal) {
64
+ return this.run(() => this.inner.prepare(endpoint, database, sql, timeoutSeconds, signal));
65
+ }
66
+ /**
67
+ * Closing is best-effort and idempotent. A token the server has already rejected means the
68
+ * handle it named is unreachable anyway, so there is nothing a replay could still release.
69
+ */
70
+ closePrepared(endpoint, database, sql, signal) {
71
+ return this.inner.closePrepared(endpoint, database, sql, signal);
72
+ }
73
+ ping(endpoint, timeoutSeconds, signal) {
74
+ return this.run(() => this.inner.ping(endpoint, timeoutSeconds, signal));
75
+ }
76
+ createDatabase(endpoint, database, ifNotExists, timeoutSeconds, signal) {
77
+ return this.run(() => this.inner.createDatabase(endpoint, database, ifNotExists, timeoutSeconds, signal));
78
+ }
79
+ createBranchDatabase(endpoint, branchName, sourceDatabaseName, ifNotExists, timeoutSeconds, signal) {
80
+ return this.run(() => this.inner.createBranchDatabase(endpoint, branchName, sourceDatabaseName, ifNotExists, timeoutSeconds, signal));
81
+ }
82
+ dropDatabase(endpoint, database, timeoutSeconds, signal) {
83
+ return this.run(() => this.inner.dropDatabase(endpoint, database, timeoutSeconds, signal));
84
+ }
85
+ showBranches(endpoint, database, timeoutSeconds, signal) {
86
+ return this.run(() => this.inner.showBranches(endpoint, database, timeoutSeconds, signal));
87
+ }
88
+ showAncestors(endpoint, database, timeoutSeconds, signal) {
89
+ return this.run(() => this.inner.showAncestors(endpoint, database, timeoutSeconds, signal));
90
+ }
91
+ close() {
92
+ return this.inner.close();
93
+ }
94
+ async run(operation) {
95
+ // Read before the call, so a refresh by a concurrent caller is not thrown away: only the token
96
+ // this attempt actually presented is discarded.
97
+ let presented = this.auth.currentToken;
98
+ try {
99
+ return await operation();
100
+ }
101
+ catch (error) {
102
+ if (!this.isRenewable(error))
103
+ throw error;
104
+ // The cache is normally warm before the call. On a client's first statement it is not: the
105
+ // inner transport mints the token during the call, and the read above returned undefined.
106
+ // Falling back to the token the provider holds now discards the one that call actually
107
+ // presented, instead of invalidating nothing and replaying with the same rejected token.
108
+ presented ??= this.auth.currentToken;
109
+ if (presented === undefined)
110
+ throw error;
111
+ this.auth.invalidate(presented);
112
+ }
113
+ return operation();
114
+ }
115
+ isRenewable(error) {
116
+ return CamusError.is(error) && error.code === CamusErrorCode.AuthenticationFailed && this.auth.canRenew;
117
+ }
118
+ }
119
+ //# sourceMappingURL=authenticating-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticating-transport.js","sourceRoot":"","sources":["../../src/transport/authenticating-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import type { CamusResultSet } from '../result-set.js';
8
+ import type { CamusBranchRow } from './transport.js';
9
+ /**
10
+ * The branch statements, composed as SQL, and the mapping of what they return.
11
+ *
12
+ * Database branching has no REST route of its own: the server implements it only as SQL, so both
13
+ * transports compose the statement and send it down the ordinary DDL and query routes. This module
14
+ * holds that composition once, so the two transports cannot spell a statement differently.
15
+ *
16
+ * A database name is a bare identifier in the grammar, so it is backtick-delimited here, and a name
17
+ * carrying a backtick is refused rather than doubled — the server trims the delimiters instead of
18
+ * decoding a doubled backtick, so doubling would neutralize nothing. These statements are among the
19
+ * few this driver composes as text, and a database name can come from an application's own input.
20
+ */
21
+ /** `CREATE DATABASE <branch> BRANCH FROM <source>`. */
22
+ export declare function createBranchDatabaseSql(branchName: string, sourceDatabaseName: string, ifNotExists: boolean): string;
23
+ /** `CREATE DATABASE <name>`. */
24
+ export declare function createDatabaseSql(database: string, ifNotExists: boolean): string;
25
+ /** `DROP DATABASE <name>`. */
26
+ export declare function dropDatabaseSql(database: string): string;
27
+ /** `SHOW BRANCHES FROM <name>`. */
28
+ export declare function showBranchesSql(database: string): string;
29
+ /** `SHOW ANCESTORS FROM <name>`. */
30
+ export declare function showAncestorsSql(database: string): string;
31
+ /**
32
+ * Reads what a branch listing returned.
33
+ *
34
+ * `SHOW BRANCHES` emits `[database, id, depth, parent, fork_timestamp]`, and `SHOW ANCESTORS` emits
35
+ * the same without `parent`. The columns are read by name, so either shape reconstructs, and a
36
+ * server that adds a column changes nothing here.
37
+ */
38
+ export declare function mapBranchRows(result: CamusResultSet): CamusBranchRow[];
39
+ //# sourceMappingURL=branch-statements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-statements.d.ts","sourceRoot":"","sources":["../../src/transport/branch-statements.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;;;;;;;;GAWG;AAEH,uDAAuD;AACvD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,OAAO,GACnB,MAAM,CAIR;AAED,gCAAgC;AAChC,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAIhF;AAED,8BAA8B;AAC9B,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,CA2BtE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * This file is part of CamusDB
3
+ *
4
+ * For the full copyright and license information, please view the LICENSE.txt
5
+ * file that was distributed with this source code.
6
+ */
7
+ import { delimitIdentifier } from '../sql-syntax.js';
8
+ /**
9
+ * The branch statements, composed as SQL, and the mapping of what they return.
10
+ *
11
+ * Database branching has no REST route of its own: the server implements it only as SQL, so both
12
+ * transports compose the statement and send it down the ordinary DDL and query routes. This module
13
+ * holds that composition once, so the two transports cannot spell a statement differently.
14
+ *
15
+ * A database name is a bare identifier in the grammar, so it is backtick-delimited here, and a name
16
+ * carrying a backtick is refused rather than doubled — the server trims the delimiters instead of
17
+ * decoding a doubled backtick, so doubling would neutralize nothing. These statements are among the
18
+ * few this driver composes as text, and a database name can come from an application's own input.
19
+ */
20
+ /** `CREATE DATABASE <branch> BRANCH FROM <source>`. */
21
+ export function createBranchDatabaseSql(branchName, sourceDatabaseName, ifNotExists) {
22
+ const prefix = ifNotExists ? 'CREATE DATABASE IF NOT EXISTS ' : 'CREATE DATABASE ';
23
+ return `${prefix}${quote(branchName)} BRANCH FROM ${quote(sourceDatabaseName)}`;
24
+ }
25
+ /** `CREATE DATABASE <name>`. */
26
+ export function createDatabaseSql(database, ifNotExists) {
27
+ const prefix = ifNotExists ? 'CREATE DATABASE IF NOT EXISTS ' : 'CREATE DATABASE ';
28
+ return `${prefix}${quote(database)}`;
29
+ }
30
+ /** `DROP DATABASE <name>`. */
31
+ export function dropDatabaseSql(database) {
32
+ return `DROP DATABASE ${quote(database)}`;
33
+ }
34
+ /** `SHOW BRANCHES FROM <name>`. */
35
+ export function showBranchesSql(database) {
36
+ return `SHOW BRANCHES FROM ${quote(database)}`;
37
+ }
38
+ /** `SHOW ANCESTORS FROM <name>`. */
39
+ export function showAncestorsSql(database) {
40
+ return `SHOW ANCESTORS FROM ${quote(database)}`;
41
+ }
42
+ /**
43
+ * Reads what a branch listing returned.
44
+ *
45
+ * `SHOW BRANCHES` emits `[database, id, depth, parent, fork_timestamp]`, and `SHOW ANCESTORS` emits
46
+ * the same without `parent`. The columns are read by name, so either shape reconstructs, and a
47
+ * server that adds a column changes nothing here.
48
+ */
49
+ export function mapBranchRows(result) {
50
+ const index = new Map();
51
+ result.columnNames.forEach((name, position) => index.set(name, position));
52
+ const rows = [];
53
+ for (let r = 0; r < result.rowCount; r++) {
54
+ const text = (name) => {
55
+ const column = index.get(name);
56
+ if (column === undefined)
57
+ return undefined;
58
+ return result.cell(r, column).strValue ?? undefined;
59
+ };
60
+ const depthColumn = index.get('depth');
61
+ rows.push({
62
+ database: text('database'),
63
+ id: text('id'),
64
+ depth: depthColumn === undefined ? 0 : Number(result.cell(r, depthColumn).longValue ?? 0n),
65
+ parent: text('parent'),
66
+ forkTimestamp: text('fork_timestamp'),
67
+ });
68
+ }
69
+ return rows;
70
+ }
71
+ function quote(name) {
72
+ return delimitIdentifier(name, 'name');
73
+ }
74
+ //# sourceMappingURL=branch-statements.js.map