@dalgo/bigquery 0.0.0-stage → 0.3.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.
- package/LICENSE +21 -0
- package/README.md +472 -2
- package/dist/analytical/canonical.d.ts +10 -0
- package/dist/analytical/canonical.d.ts.map +1 -0
- package/dist/analytical/canonical.js +50 -0
- package/dist/analytical/client.d.ts +84 -0
- package/dist/analytical/client.d.ts.map +1 -0
- package/dist/analytical/client.js +730 -0
- package/dist/analytical/deadline.d.ts +13 -0
- package/dist/analytical/deadline.d.ts.map +1 -0
- package/dist/analytical/deadline.js +23 -0
- package/dist/analytical/google-identity.d.ts +40 -0
- package/dist/analytical/google-identity.d.ts.map +1 -0
- package/dist/analytical/google-identity.js +158 -0
- package/dist/analytical/ledger.d.ts +80 -0
- package/dist/analytical/ledger.d.ts.map +1 -0
- package/dist/analytical/ledger.js +89 -0
- package/dist/analytical/metadata-client.d.ts +66 -0
- package/dist/analytical/metadata-client.d.ts.map +1 -0
- package/dist/analytical/metadata-client.js +247 -0
- package/dist/analytical/metadata-harness.d.ts +28 -0
- package/dist/analytical/metadata-harness.d.ts.map +1 -0
- package/dist/analytical/metadata-harness.js +127 -0
- package/dist/analytical/metadata.d.ts +8 -0
- package/dist/analytical/metadata.d.ts.map +1 -0
- package/dist/analytical/metadata.js +155 -0
- package/dist/analytical/protocol.d.ts +157 -0
- package/dist/analytical/protocol.d.ts.map +1 -0
- package/dist/analytical/protocol.js +320 -0
- package/dist/analytical/public-metadata.d.ts +29 -0
- package/dist/analytical/public-metadata.d.ts.map +1 -0
- package/dist/analytical/public-metadata.js +70 -0
- package/dist/analytical/transport.d.ts +39 -0
- package/dist/analytical/transport.d.ts.map +1 -0
- package/dist/analytical/transport.js +224 -0
- package/dist/analytical/values.d.ts +18 -0
- package/dist/analytical/values.d.ts.map +1 -0
- package/dist/analytical/values.js +157 -0
- package/dist/analytical/wire.d.ts +26 -0
- package/dist/analytical/wire.d.ts.map +1 -0
- package/dist/analytical/wire.js +141 -0
- package/dist/analytical.d.ts +27 -0
- package/dist/analytical.d.ts.map +1 -0
- package/dist/analytical.js +14 -0
- package/dist/database.d.ts +35 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +437 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/sql.d.ts +29 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +198 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +58 -4
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { AnalyticalError, fail, parseJSON } from "./wire.js";
|
|
2
|
+
import { operationDeadline } from "./deadline.js";
|
|
3
|
+
import { same } from "./metadata.js";
|
|
4
|
+
import { validatePrincipal } from "./protocol.js";
|
|
5
|
+
export const realClock = {
|
|
6
|
+
now: () => Date.now(), sleep: (ms, signal) => new Promise((resolve, reject) => {
|
|
7
|
+
if (signal?.aborted) {
|
|
8
|
+
reject(new AnalyticalError("local_stopped"));
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const abort = () => { clearTimeout(timer); reject(new AnalyticalError("local_stopped")); };
|
|
12
|
+
const timer = setTimeout(() => { signal?.removeEventListener("abort", abort); resolve(); }, ms);
|
|
13
|
+
signal?.addEventListener("abort", abort, {
|
|
14
|
+
once: true
|
|
15
|
+
});
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
// Bound injected providers/transports too: AbortSignal cooperation alone cannot
|
|
19
|
+
// prove a deadline when an integration accidentally ignores cancellation.
|
|
20
|
+
export function abortable(promise, signal) {
|
|
21
|
+
if (signal.aborted)
|
|
22
|
+
return Promise.reject(new AnalyticalError("local_stopped"));
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const abort = () => reject(new AnalyticalError("local_stopped"));
|
|
25
|
+
signal.addEventListener("abort", abort, {
|
|
26
|
+
once: true
|
|
27
|
+
});
|
|
28
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort)).catch(() => { });
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export class Transport {
|
|
32
|
+
#provider;
|
|
33
|
+
#fetch;
|
|
34
|
+
#clock;
|
|
35
|
+
constructor(provider, fetcher, clock) { this.#provider = provider; this.#fetch = fetcher; this.#clock = clock; }
|
|
36
|
+
async verify(principal, deadline, signal, rebind = false) {
|
|
37
|
+
if (signal?.aborted || this.#clock.now() >= deadline)
|
|
38
|
+
fail("local_stopped");
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const abort = () => controller.abort();
|
|
41
|
+
signal?.addEventListener("abort", abort, {
|
|
42
|
+
once: true
|
|
43
|
+
});
|
|
44
|
+
const timer = setTimeout(abort, deadline - this.#clock.now());
|
|
45
|
+
try {
|
|
46
|
+
let identity;
|
|
47
|
+
try {
|
|
48
|
+
identity = await abortable(this.#provider.authorize(controller.signal), controller.signal);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof AnalyticalError)
|
|
52
|
+
throw error;
|
|
53
|
+
fail("auth_required");
|
|
54
|
+
}
|
|
55
|
+
validatePrincipal(identity.principal);
|
|
56
|
+
if (rebind ? identity.principal.kind !== principal.kind || identity.principal.subject !== principal.subject : !same(identity.principal, principal))
|
|
57
|
+
fail("approval_changed");
|
|
58
|
+
if (typeof identity.accessToken !== "string" || !identity.accessToken || /[\r\n]/u.test(identity.accessToken))
|
|
59
|
+
fail("auth_required");
|
|
60
|
+
if (!Number.isSafeInteger(identity.expiresAt) || identity.expiresAt <= this.#clock.now())
|
|
61
|
+
fail("auth_expired");
|
|
62
|
+
if (!identity.read)
|
|
63
|
+
fail("scope_missing");
|
|
64
|
+
if (controller.signal.aborted || this.#clock.now() >= deadline)
|
|
65
|
+
fail("local_stopped");
|
|
66
|
+
return {
|
|
67
|
+
...identity.principal
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
signal?.removeEventListener("abort", abort);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async call(scope, method, path, query, body, control = false, cancel = false, beforeDispatch) {
|
|
76
|
+
if (!/^projects\/[A-Za-z0-9_-]+\/(?:datasets|queries|jobs)(?:\/[A-Za-z0-9_%./-]+)*$/u.test(path) || path.includes(".."))
|
|
77
|
+
fail("invalid_input");
|
|
78
|
+
const controlDeadline = this.#clock.now() + Math.min(15000, scope.bounds.httpMs);
|
|
79
|
+
const callerDeadline = control ? Math.min(scope.callerDeadline ?? Number.MAX_SAFE_INTEGER, controlDeadline) : scope.callerDeadline;
|
|
80
|
+
for (let attempt = 0; attempt < (method === "GET" ? 3 : 1); attempt += 1) {
|
|
81
|
+
if (scope.signal?.aborted)
|
|
82
|
+
fail("local_stopped");
|
|
83
|
+
const now = this.#clock.now();
|
|
84
|
+
const remaining = await scope.remaining();
|
|
85
|
+
const deadline = operationDeadline({
|
|
86
|
+
now, executionDeadline: scope.executionDeadline, httpLimitMs: scope.bounds.httpMs, control, bytesRemaining: BigInt(remaining), ...(callerDeadline === undefined ? {} : {
|
|
87
|
+
callerDeadline
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const abort = () => controller.abort();
|
|
92
|
+
scope.signal?.addEventListener("abort", abort, {
|
|
93
|
+
once: true
|
|
94
|
+
});
|
|
95
|
+
const timer = setTimeout(abort, deadline - now);
|
|
96
|
+
let response;
|
|
97
|
+
let identity;
|
|
98
|
+
try {
|
|
99
|
+
try {
|
|
100
|
+
identity = await abortable(this.#provider.authorize(controller.signal), controller.signal);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error instanceof AnalyticalError)
|
|
104
|
+
throw error;
|
|
105
|
+
fail("auth_required");
|
|
106
|
+
}
|
|
107
|
+
validatePrincipal(identity.principal);
|
|
108
|
+
if (!same(identity.principal, scope.principal))
|
|
109
|
+
fail("approval_changed");
|
|
110
|
+
if (typeof identity.accessToken !== "string" || identity.accessToken.length < 1 || /[\r\n]/u.test(identity.accessToken))
|
|
111
|
+
fail("auth_required");
|
|
112
|
+
if (!Number.isSafeInteger(identity.expiresAt) || identity.expiresAt <= this.#clock.now())
|
|
113
|
+
fail("auth_expired");
|
|
114
|
+
if (!identity.read || (cancel && !identity.cancel))
|
|
115
|
+
fail("scope_missing");
|
|
116
|
+
if (controller.signal.aborted || this.#clock.now() >= deadline)
|
|
117
|
+
fail("local_stopped");
|
|
118
|
+
const url = `https://bigquery.googleapis.com/bigquery/v2/${path}${Object.keys(query).length === 0 ? "" : "?" + new URLSearchParams(query)}`;
|
|
119
|
+
const requestBody = body === undefined ? undefined : JSON.stringify(body);
|
|
120
|
+
await scope.beforeDispatch?.();
|
|
121
|
+
if (controller.signal.aborted || this.#clock.now() >= deadline)
|
|
122
|
+
fail("local_stopped");
|
|
123
|
+
beforeDispatch?.();
|
|
124
|
+
try {
|
|
125
|
+
response = await abortable(this.#fetch(url, {
|
|
126
|
+
method, headers: {
|
|
127
|
+
Authorization: `Bearer ${identity.accessToken}`, Accept: "application/json", ...(body === undefined ? {} : {
|
|
128
|
+
"Content-Type": "application/json"
|
|
129
|
+
})
|
|
130
|
+
}, redirect: "error", credentials: "omit", cache: "no-store", signal: controller.signal, ...(requestBody === undefined ? {} : {
|
|
131
|
+
body: requestBody
|
|
132
|
+
})
|
|
133
|
+
}), controller.signal);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
if (controller.signal.aborted)
|
|
137
|
+
fail("local_stopped");
|
|
138
|
+
fail(method === "POST" ? cancel ? "cancellation_unknown" : "submission_unknown" : "remote_failed");
|
|
139
|
+
}
|
|
140
|
+
if (controller.signal.aborted || this.#clock.now() >= deadline)
|
|
141
|
+
fail("local_stopped");
|
|
142
|
+
if (response.redirected || response.status >= 300 && response.status < 400)
|
|
143
|
+
fail(method === "POST" ? cancel ? "cancellation_unknown" : "submission_unknown" : "remote_failed");
|
|
144
|
+
const chunks = [];
|
|
145
|
+
let length = 0;
|
|
146
|
+
const reader = response.body?.getReader();
|
|
147
|
+
if (reader !== undefined) {
|
|
148
|
+
try {
|
|
149
|
+
while (true) {
|
|
150
|
+
const item = await abortable(reader.read(), controller.signal);
|
|
151
|
+
if (item.done)
|
|
152
|
+
break;
|
|
153
|
+
const chunk = item.value;
|
|
154
|
+
await scope.debit(chunk.byteLength);
|
|
155
|
+
length += chunk.byteLength;
|
|
156
|
+
if (length > scope.bounds.responseBytes || controller.signal.aborted || this.#clock.now() >= deadline) {
|
|
157
|
+
void reader.cancel().catch(() => { });
|
|
158
|
+
fail(length > scope.bounds.responseBytes ? "response_limit" : "local_stopped");
|
|
159
|
+
}
|
|
160
|
+
chunks.push(chunk);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
try {
|
|
165
|
+
void reader.cancel().catch(() => { });
|
|
166
|
+
}
|
|
167
|
+
catch { /* no provider body retained */ }
|
|
168
|
+
if (error instanceof AnalyticalError)
|
|
169
|
+
throw error;
|
|
170
|
+
fail(method === "POST" ? cancel ? "cancellation_unknown" : "submission_unknown" : "remote_failed");
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
reader.releaseLock();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const raw = new Uint8Array(length);
|
|
177
|
+
let offset = 0;
|
|
178
|
+
for (const chunk of chunks) {
|
|
179
|
+
raw.set(chunk, offset);
|
|
180
|
+
offset += chunk.length;
|
|
181
|
+
}
|
|
182
|
+
if (controller.signal.aborted || this.#clock.now() >= deadline)
|
|
183
|
+
fail("local_stopped");
|
|
184
|
+
if (response.status >= 200 && response.status < 300)
|
|
185
|
+
return parseJSON(raw, scope.bounds.responseBytes);
|
|
186
|
+
// Provider bodies are charged but never copied into public errors/logs.
|
|
187
|
+
if (method === "GET" && [429, 500, 502, 503, 504].includes(response.status) && attempt < 2) {
|
|
188
|
+
let delay = 100 * 2 ** attempt;
|
|
189
|
+
const retry = response.headers.get("Retry-After");
|
|
190
|
+
if (retry !== null) {
|
|
191
|
+
if (/^[0-9]+$/u.test(retry))
|
|
192
|
+
delay = Number(retry) * 1000;
|
|
193
|
+
else {
|
|
194
|
+
const date = Date.parse(retry);
|
|
195
|
+
if (Number.isFinite(date))
|
|
196
|
+
delay = Math.max(0, date - this.#clock.now());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const next = operationDeadline({
|
|
200
|
+
now: this.#clock.now(), executionDeadline: scope.executionDeadline, httpLimitMs: scope.bounds.httpMs, control, bytesRemaining: BigInt(await scope.remaining()), ...(callerDeadline === undefined ? {} : {
|
|
201
|
+
callerDeadline
|
|
202
|
+
})
|
|
203
|
+
});
|
|
204
|
+
if (this.#clock.now() + delay >= next)
|
|
205
|
+
fail("local_stopped");
|
|
206
|
+
await this.#clock.sleep(delay, scope.signal);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (response.status === 401)
|
|
210
|
+
fail("auth_expired");
|
|
211
|
+
if (response.status === 403)
|
|
212
|
+
fail("scope_missing");
|
|
213
|
+
if (response.status === 404)
|
|
214
|
+
fail(control ? "remote_failed" : "result_expired");
|
|
215
|
+
fail(method === "POST" ? cancel ? "cancellation_unknown" : "submission_unknown" : "remote_failed");
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
clearTimeout(timer);
|
|
219
|
+
scope.signal?.removeEventListener("abort", abort);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return fail("remote_failed");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface AnalyticalField {
|
|
2
|
+
readonly name?: string;
|
|
3
|
+
readonly type: string;
|
|
4
|
+
readonly mode?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AnalyticalCell {
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly value: string | boolean | null;
|
|
9
|
+
}
|
|
10
|
+
/** Preserve SQL NULL, exact decimal strings, JSON text and keyless row order.
|
|
11
|
+
* TIMESTAMP values use signed epoch microseconds in the foundation contract.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeScalar(field: AnalyticalField, value: unknown): AnalyticalCell;
|
|
14
|
+
/** Fatal UTF-8 boundary for scalar bytes; never normalize replacement decoding. */
|
|
15
|
+
export declare function normalizeScalarBytes(field: AnalyticalField, raw: Uint8Array): AnalyticalCell;
|
|
16
|
+
/** Schema eligibility/precision metadata is a separate gate, not inferred here. */
|
|
17
|
+
export declare function decodeRows(raw: Uint8Array, fields: readonly AnalyticalField[], limit?: number): readonly (readonly AnalyticalCell[])[];
|
|
18
|
+
//# sourceMappingURL=values.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/analytical/values.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC1G,MAAM,WAAW,cAAc;IAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;CAAE;AAsClG;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,cAAc,CAyEtF;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,UAAU,GAAG,cAAc,CAE5F;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,eAAe,EAAE,EAAE,KAAK,SAAqB,GAAG,SAAS,CAAC,SAAS,cAAc,EAAE,CAAC,EAAE,CAYlJ"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { assertUnicodeScalar, decodeUtf8, fail, isObject, MAX_RESPONSE_BYTES, parseJSON } from "./wire.js";
|
|
2
|
+
const integer = /^-?(0|[1-9][0-9]*)$/u;
|
|
3
|
+
const decimal = /^-?[0-9]+(\.[0-9]+)?$/u;
|
|
4
|
+
const float = /^[+-]?([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?$/u;
|
|
5
|
+
const clock = /^([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,6})?$/u;
|
|
6
|
+
const supported = new Set("INT64 NUMERIC BIGNUMERIC FLOAT64 BOOL STRING BYTES DATE TIME DATETIME TIMESTAMP JSON".split(" "));
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
/** RegExp $ can match before a trailing newline in JS; require every code unit. */
|
|
9
|
+
function matches(expression, value) {
|
|
10
|
+
const match = expression.exec(value);
|
|
11
|
+
return match !== null && match[0].length === value.length;
|
|
12
|
+
}
|
|
13
|
+
function validDate(value) {
|
|
14
|
+
if (!matches(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u, value))
|
|
15
|
+
return false;
|
|
16
|
+
const year = Number(value.slice(0, 4));
|
|
17
|
+
const month = Number(value.slice(5, 7));
|
|
18
|
+
const day = Number(value.slice(8, 10));
|
|
19
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
20
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
21
|
+
return year >= 1 && year <= 9999 && month >= 1 && month <= 12 && day >= 1 && day <= (days[month - 1] ?? 0);
|
|
22
|
+
}
|
|
23
|
+
function validClock(value) {
|
|
24
|
+
if (!matches(clock, value))
|
|
25
|
+
return false;
|
|
26
|
+
return Number(value.slice(0, 2)) < 24 && Number(value.slice(3, 5)) < 60 && Number(value.slice(6, 8)) < 60;
|
|
27
|
+
}
|
|
28
|
+
function validBase64(value) {
|
|
29
|
+
if (!matches(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u, value))
|
|
30
|
+
return false;
|
|
31
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
32
|
+
if (value.endsWith("=="))
|
|
33
|
+
return (alphabet.indexOf(value.at(-3) ?? "") & 15) === 0;
|
|
34
|
+
if (value.endsWith("="))
|
|
35
|
+
return (alphabet.indexOf(value.at(-2) ?? "") & 3) === 0;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
/** Preserve SQL NULL, exact decimal strings, JSON text and keyless row order.
|
|
39
|
+
* TIMESTAMP values use signed epoch microseconds in the foundation contract.
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeScalar(field, value) {
|
|
42
|
+
if (typeof field.type !== "string")
|
|
43
|
+
fail("invalid_input");
|
|
44
|
+
let type = field.type.toUpperCase();
|
|
45
|
+
type = { INTEGER: "INT64", FLOAT: "FLOAT64", BOOLEAN: "BOOL" }[type] ?? type;
|
|
46
|
+
if (!supported.has(type) || ![undefined, "", "NULLABLE", "REQUIRED"].includes(field.mode))
|
|
47
|
+
fail("unsupported_type");
|
|
48
|
+
if (value === null) {
|
|
49
|
+
if (field.mode === "REQUIRED")
|
|
50
|
+
fail("malformed_wire");
|
|
51
|
+
return { type, value: null };
|
|
52
|
+
}
|
|
53
|
+
if (typeof value !== "string")
|
|
54
|
+
fail("malformed_wire");
|
|
55
|
+
assertUnicodeScalar(value);
|
|
56
|
+
if (encoder.encode(value).byteLength > 1024 * 1024)
|
|
57
|
+
fail("response_limit");
|
|
58
|
+
let normalized = value;
|
|
59
|
+
switch (type) {
|
|
60
|
+
case "INT64":
|
|
61
|
+
case "TIMESTAMP": {
|
|
62
|
+
if (!matches(integer, value))
|
|
63
|
+
fail("unsupported_value");
|
|
64
|
+
const number = BigInt(value);
|
|
65
|
+
if (number < -9223372036854775808n || number > 9223372036854775807n)
|
|
66
|
+
fail("unsupported_value");
|
|
67
|
+
if (type === "TIMESTAMP" && (number < -62135596800000000n || number > 253402300799999999n))
|
|
68
|
+
fail("unsupported_value");
|
|
69
|
+
normalized = number.toString();
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "NUMERIC":
|
|
73
|
+
case "BIGNUMERIC": {
|
|
74
|
+
if (!matches(decimal, value))
|
|
75
|
+
fail("unsupported_value");
|
|
76
|
+
const negative = value.startsWith("-");
|
|
77
|
+
const parts = (negative ? value.slice(1) : value).split(".");
|
|
78
|
+
const whole = (parts[0] ?? "").replace(/^0+/u, "") || "0";
|
|
79
|
+
const fraction = parts[1] ?? "";
|
|
80
|
+
const scale = fraction.length;
|
|
81
|
+
if (scale > (type === "NUMERIC" ? 9 : 38) || (whole === "0" ? 0 : whole.length) > (type === "NUMERIC" ? 29 : 39))
|
|
82
|
+
fail("unsupported_value");
|
|
83
|
+
if (type === "BIGNUMERIC") {
|
|
84
|
+
const scaled = BigInt(whole + fraction) * 10n ** BigInt(38 - scale);
|
|
85
|
+
if (scaled > (1n << 255n) - (negative ? 0n : 1n))
|
|
86
|
+
fail("unsupported_value");
|
|
87
|
+
}
|
|
88
|
+
const trimmed = fraction.replace(/0+$/u, "");
|
|
89
|
+
normalized = whole + (trimmed === "" ? "" : `.${trimmed}`);
|
|
90
|
+
if (negative && normalized !== "0")
|
|
91
|
+
normalized = `-${normalized}`;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "FLOAT64": {
|
|
95
|
+
if (!matches(float, value))
|
|
96
|
+
fail("unsupported_value");
|
|
97
|
+
const number = Number(value);
|
|
98
|
+
if (!Number.isFinite(number))
|
|
99
|
+
fail("unsupported_value");
|
|
100
|
+
normalized = number.toString(); // ECMAScript NumberToString includes -0 -> 0.
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "BOOL":
|
|
104
|
+
if (value !== "true" && value !== "false")
|
|
105
|
+
fail("unsupported_value");
|
|
106
|
+
normalized = value === "true";
|
|
107
|
+
break;
|
|
108
|
+
case "BYTES":
|
|
109
|
+
if (!validBase64(value))
|
|
110
|
+
fail("unsupported_value");
|
|
111
|
+
break;
|
|
112
|
+
case "DATE":
|
|
113
|
+
if (!validDate(value))
|
|
114
|
+
fail("unsupported_value");
|
|
115
|
+
break;
|
|
116
|
+
case "TIME":
|
|
117
|
+
if (!validClock(value))
|
|
118
|
+
fail("unsupported_value");
|
|
119
|
+
break;
|
|
120
|
+
case "DATETIME": {
|
|
121
|
+
let parts = value.split("T");
|
|
122
|
+
if (parts.length !== 2)
|
|
123
|
+
parts = value.split(" ");
|
|
124
|
+
if (parts.length !== 2 || !validDate(parts[0] ?? "") || !validClock(parts[1] ?? ""))
|
|
125
|
+
fail("unsupported_value");
|
|
126
|
+
normalized = `${parts[0]}T${parts[1]}`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "JSON":
|
|
130
|
+
parseJSON(encoder.encode(value), 1024 * 1024);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
return { type, value: normalized };
|
|
134
|
+
}
|
|
135
|
+
/** Fatal UTF-8 boundary for scalar bytes; never normalize replacement decoding. */
|
|
136
|
+
export function normalizeScalarBytes(field, raw) {
|
|
137
|
+
return normalizeScalar(field, decodeUtf8(raw));
|
|
138
|
+
}
|
|
139
|
+
/** Schema eligibility/precision metadata is a separate gate, not inferred here. */
|
|
140
|
+
export function decodeRows(raw, fields, limit = MAX_RESPONSE_BYTES) {
|
|
141
|
+
if (fields.length < 1 || fields.length > 128)
|
|
142
|
+
fail("invalid_input");
|
|
143
|
+
const value = parseJSON(raw, limit);
|
|
144
|
+
if (!Array.isArray(value))
|
|
145
|
+
fail("malformed_wire");
|
|
146
|
+
if (value.length > 1000)
|
|
147
|
+
fail("response_limit");
|
|
148
|
+
return value.map((row) => {
|
|
149
|
+
if (!isObject(row) || Object.keys(row).length !== 1 || !Array.isArray(row.f) || row.f.length !== fields.length)
|
|
150
|
+
fail("malformed_wire");
|
|
151
|
+
return row.f.map((cell, index) => {
|
|
152
|
+
if (!isObject(cell) || Object.keys(cell).length !== 1 || !Object.hasOwn(cell, "v"))
|
|
153
|
+
fail("malformed_wire");
|
|
154
|
+
return normalizeScalar(fields[index], cell.v);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Stable sanitized failures never retain provider bodies or query values. */
|
|
2
|
+
export type AnalyticalErrorCode = "invalid_input" | "malformed_wire" | "response_limit" | "unsupported_type" | "unsupported_value" | "local_stopped" | "unsupported_query" | "policy_denied" | "no_policies" | "auth_required" | "auth_expired" | "scope_missing" | "source_ineligible" | "source_changed" | "approval_required" | "approval_changed" | "estimate_missing" | "cap_exceeded" | "budget_exhausted" | "schema_changed" | "cursor_invalid" | "result_expired" | "submission_unknown" | "remote_failed" | "cancellation_unknown";
|
|
3
|
+
export declare class AnalyticalError extends Error {
|
|
4
|
+
readonly code: AnalyticalErrorCode;
|
|
5
|
+
constructor(code: AnalyticalErrorCode);
|
|
6
|
+
}
|
|
7
|
+
export declare function fail(code: AnalyticalErrorCode): never;
|
|
8
|
+
export declare const MAX_RESPONSE_BYTES: number;
|
|
9
|
+
export declare const MAX_DEPTH = 32;
|
|
10
|
+
/** A JSON number token, preserved before any floating-point conversion. */
|
|
11
|
+
export declare class JsonNumber {
|
|
12
|
+
readonly text: string;
|
|
13
|
+
constructor(text: string);
|
|
14
|
+
}
|
|
15
|
+
export type JsonValue = null | boolean | string | JsonNumber | JsonValue[] | {
|
|
16
|
+
[key: string]: JsonValue;
|
|
17
|
+
};
|
|
18
|
+
export declare function isObject(value: JsonValue): value is {
|
|
19
|
+
[key: string]: JsonValue;
|
|
20
|
+
};
|
|
21
|
+
/** JS direct strings must contain Unicode scalar values before UTF-8 encoding. */
|
|
22
|
+
export declare function assertUnicodeScalar(value: string): void;
|
|
23
|
+
export declare function decodeUtf8(raw: Uint8Array): string;
|
|
24
|
+
/** Callers must cap decompressed reads before allocating the input buffer. */
|
|
25
|
+
export declare function parseJSON(raw: Uint8Array, limit: number): JsonValue;
|
|
26
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/analytical/wire.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,mBAAmB,GAAG,eAAe,GAAG,mBAAmB,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,GAAG,cAAc,GAAG,eAAe,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,cAAc,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,oBAAoB,GAAG,eAAe,GAAG,sBAAsB,CAAC;AAE5gB,qBAAa,eAAgB,SAAQ,KAAK;aACL,IAAI,EAAE,mBAAmB;gBAAzB,IAAI,EAAE,mBAAmB;CAI7D;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,mBAAmB,GAAG,KAAK,CAErD;AAED,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AACnD,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,2EAA2E;AAC3E,qBAAa,UAAU;aACc,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAChD;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE1G,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAEhF;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAUvD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAKlD;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CA8DnE"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export class AnalyticalError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code) {
|
|
4
|
+
super(code);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = "AnalyticalError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function fail(code) {
|
|
10
|
+
throw new AnalyticalError(code);
|
|
11
|
+
}
|
|
12
|
+
export const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
13
|
+
export const MAX_DEPTH = 32;
|
|
14
|
+
/** A JSON number token, preserved before any floating-point conversion. */
|
|
15
|
+
export class JsonNumber {
|
|
16
|
+
text;
|
|
17
|
+
constructor(text) {
|
|
18
|
+
this.text = text;
|
|
19
|
+
Object.freeze(this);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function isObject(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JsonNumber);
|
|
24
|
+
}
|
|
25
|
+
/** JS direct strings must contain Unicode scalar values before UTF-8 encoding. */
|
|
26
|
+
export function assertUnicodeScalar(value) {
|
|
27
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
28
|
+
const code = value.charCodeAt(i);
|
|
29
|
+
if (code >= 0xdc00 && code <= 0xdfff)
|
|
30
|
+
fail("malformed_wire");
|
|
31
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
32
|
+
i += 1;
|
|
33
|
+
const low = value.charCodeAt(i);
|
|
34
|
+
if (!(low >= 0xdc00 && low <= 0xdfff))
|
|
35
|
+
fail("malformed_wire");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function decodeUtf8(raw) {
|
|
40
|
+
try {
|
|
41
|
+
// Preserve BOM so JSON syntax rejects it; never silently strip input bytes.
|
|
42
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return fail("malformed_wire");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Callers must cap decompressed reads before allocating the input buffer. */
|
|
49
|
+
export function parseJSON(raw, limit) {
|
|
50
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > MAX_RESPONSE_BYTES || raw.byteLength > limit)
|
|
51
|
+
fail("response_limit");
|
|
52
|
+
const text = decodeUtf8(raw);
|
|
53
|
+
let at = 0;
|
|
54
|
+
const whitespace = () => { while (" \t\r\n".includes(text[at] ?? "\0"))
|
|
55
|
+
at += 1; };
|
|
56
|
+
const string = () => {
|
|
57
|
+
const start = at;
|
|
58
|
+
at += 1;
|
|
59
|
+
while (at < text.length) {
|
|
60
|
+
const character = text[at];
|
|
61
|
+
at += 1;
|
|
62
|
+
if (character === "\\") {
|
|
63
|
+
at += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (character === '"') {
|
|
67
|
+
let result;
|
|
68
|
+
try {
|
|
69
|
+
result = JSON.parse(text.slice(start, at));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return fail("malformed_wire");
|
|
73
|
+
}
|
|
74
|
+
assertUnicodeScalar(result);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return fail("malformed_wire");
|
|
79
|
+
};
|
|
80
|
+
const value = (depth) => {
|
|
81
|
+
if (depth > MAX_DEPTH)
|
|
82
|
+
fail("response_limit");
|
|
83
|
+
whitespace();
|
|
84
|
+
const character = text[at];
|
|
85
|
+
if (character === '"')
|
|
86
|
+
return string();
|
|
87
|
+
if (character === "{" || character === "[") {
|
|
88
|
+
at += 1;
|
|
89
|
+
whitespace();
|
|
90
|
+
const object = character === "{";
|
|
91
|
+
const close = object ? "}" : "]";
|
|
92
|
+
const result = object ? Object.create(null) : [];
|
|
93
|
+
if (text[at] === close) {
|
|
94
|
+
at += 1;
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
while (true) {
|
|
98
|
+
if (object) {
|
|
99
|
+
if (text[at] !== '"')
|
|
100
|
+
fail("malformed_wire");
|
|
101
|
+
const key = string();
|
|
102
|
+
if (Object.hasOwn(result, key))
|
|
103
|
+
fail("malformed_wire");
|
|
104
|
+
whitespace();
|
|
105
|
+
if (text[at] !== ":")
|
|
106
|
+
fail("malformed_wire");
|
|
107
|
+
at += 1;
|
|
108
|
+
result[key] = value(depth + 1);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
result.push(value(depth + 1));
|
|
112
|
+
}
|
|
113
|
+
whitespace();
|
|
114
|
+
if (text[at] === close) {
|
|
115
|
+
at += 1;
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
if (text[at] !== ",")
|
|
119
|
+
fail("malformed_wire");
|
|
120
|
+
at += 1;
|
|
121
|
+
whitespace();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (const [literal, result] of [["null", null], ["true", true], ["false", false]]) {
|
|
125
|
+
if (text.startsWith(literal, at)) {
|
|
126
|
+
at += literal.length;
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const match = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/u.exec(text.slice(at));
|
|
131
|
+
if (match === null)
|
|
132
|
+
return fail("malformed_wire");
|
|
133
|
+
at += match[0].length;
|
|
134
|
+
return new JsonNumber(match[0]);
|
|
135
|
+
};
|
|
136
|
+
const result = value(0);
|
|
137
|
+
whitespace();
|
|
138
|
+
if (at !== text.length)
|
|
139
|
+
fail("malformed_wire");
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Browser-compatible A0 analytical protocol with no DALgo runtime import.
|
|
2
|
+
* Consumers inject trusted identity, protected read policy and durable session storage.
|
|
3
|
+
*/
|
|
4
|
+
export { canonicalJSON, hashPayload } from "./analytical/canonical.js";
|
|
5
|
+
export type { HashPayloadName, HashedPayload } from "./analytical/canonical.js";
|
|
6
|
+
export { operationDeadline } from "./analytical/deadline.js";
|
|
7
|
+
export type { DeadlineInput } from "./analytical/deadline.js";
|
|
8
|
+
export { decodeRows, normalizeScalar, normalizeScalarBytes } from "./analytical/values.js";
|
|
9
|
+
export type { AnalyticalCell, AnalyticalField } from "./analytical/values.js";
|
|
10
|
+
export { AnalyticalError, JsonNumber, MAX_DEPTH, MAX_RESPONSE_BYTES, parseJSON } from "./analytical/wire.js";
|
|
11
|
+
export type { AnalyticalErrorCode, JsonValue } from "./analytical/wire.js";
|
|
12
|
+
export { BigQueryAnalyticalClient, AnalyticalRun, Approval, ExecutionFailure, queryRequest } from "./analytical/client.js";
|
|
13
|
+
export type { ClientConfig, PreparedRead, OperationOptions, JobStatus, CancelResult } from "./analytical/client.js";
|
|
14
|
+
export { compileReadPlan, bounds } from "./analytical/protocol.js";
|
|
15
|
+
export type { SourceProfile, ReadQuery, ReadPlan, Parameter, CompiledPredicate, Principal, Execution, Bounds, SchemaField, Observation, JobRef, Receipt, Page } from "./analytical/protocol.js";
|
|
16
|
+
export { IndexedDBLedger, MemoryLedger } from "./analytical/ledger.js";
|
|
17
|
+
export type { Ledger, LedgerState, Preview } from "./analytical/ledger.js";
|
|
18
|
+
export type { TrustedIdentity, IdentityProvider, Clock, SafeFetch } from "./analytical/transport.js";
|
|
19
|
+
export { GoogleTokenIdentityProvider, googleAuthorizationScopes } from "./analytical/google-identity.js";
|
|
20
|
+
export type { GoogleTokenResponse, GoogleIdentitySummary, GoogleIdentityConfig } from "./analytical/google-identity.js";
|
|
21
|
+
export { BigQueryMetadataClient } from "./analytical/metadata-client.js";
|
|
22
|
+
export type { MetadataSource, MetadataConsent, MetadataCurrentBinding, MetadataLimits, MetadataClientConfig, MetadataOptions, MetadataDiscovery } from "./analytical/metadata-client.js";
|
|
23
|
+
export { MetadataFixtureHarness } from "./analytical/metadata-harness.js";
|
|
24
|
+
export type { MetadataFixtureHarnessConfig } from "./analytical/metadata-harness.js";
|
|
25
|
+
export { projectFixtureMetadata } from "./analytical/public-metadata.js";
|
|
26
|
+
export type { PublicMetadataObservation, PublicMetadataField } from "./analytical/public-metadata.js";
|
|
27
|
+
//# sourceMappingURL=analytical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytical.d.ts","sourceRoot":"","sources":["../src/analytical.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC7G,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3H,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACpH,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChM,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACrG,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AACzG,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACxH,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,sBAAsB,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzL,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,YAAY,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Browser-compatible A0 analytical protocol with no DALgo runtime import.
|
|
2
|
+
* Consumers inject trusted identity, protected read policy and durable session storage.
|
|
3
|
+
*/
|
|
4
|
+
export { canonicalJSON, hashPayload } from "./analytical/canonical.js";
|
|
5
|
+
export { operationDeadline } from "./analytical/deadline.js";
|
|
6
|
+
export { decodeRows, normalizeScalar, normalizeScalarBytes } from "./analytical/values.js";
|
|
7
|
+
export { AnalyticalError, JsonNumber, MAX_DEPTH, MAX_RESPONSE_BYTES, parseJSON } from "./analytical/wire.js";
|
|
8
|
+
export { BigQueryAnalyticalClient, AnalyticalRun, Approval, ExecutionFailure, queryRequest } from "./analytical/client.js";
|
|
9
|
+
export { compileReadPlan, bounds } from "./analytical/protocol.js";
|
|
10
|
+
export { IndexedDBLedger, MemoryLedger } from "./analytical/ledger.js";
|
|
11
|
+
export { GoogleTokenIdentityProvider, googleAuthorizationScopes } from "./analytical/google-identity.js";
|
|
12
|
+
export { BigQueryMetadataClient } from "./analytical/metadata-client.js";
|
|
13
|
+
export { MetadataFixtureHarness } from "./analytical/metadata-harness.js";
|
|
14
|
+
export { projectFixtureMetadata } from "./analytical/public-metadata.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Key, type Codec, type Database, type QueryPage, type ReadwriteTransaction, type RecordSnapshot, type StructuredQuery } from "@dalgo/core";
|
|
2
|
+
import type { BigQueryDatabaseOptions } from "./types.js";
|
|
3
|
+
export declare class BigQueryHttpError extends Error {
|
|
4
|
+
readonly status: number;
|
|
5
|
+
constructor(status: number);
|
|
6
|
+
}
|
|
7
|
+
/** A completed BigQuery job reported a failure. The API error body is intentionally not retained. */
|
|
8
|
+
/**
|
|
9
|
+
* DALgo adapter over BigQuery's REST jobs.query and jobs.getQueryResults APIs.
|
|
10
|
+
* It intentionally never accepts a service-account key or embeds access tokens.
|
|
11
|
+
*/
|
|
12
|
+
export declare class BigQueryDatabase implements Database {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(options: BigQueryDatabaseOptions);
|
|
15
|
+
get<T>(key: Key, codec?: Codec<T>): Promise<RecordSnapshot<T>>;
|
|
16
|
+
getMany<T>(keys: readonly Key[], codec?: Codec<T>): Promise<readonly RecordSnapshot<T>[]>;
|
|
17
|
+
query<T>(query: StructuredQuery<T>): Promise<QueryPage<T>>;
|
|
18
|
+
/** BigQuery cannot atomically enforce a unique key, so DALgo insert is rejected. */
|
|
19
|
+
insert<T>(_key: Key, _data: T, _codec?: Codec<T>): Promise<void>;
|
|
20
|
+
/** BigQuery MERGE cannot prove DALgo replacement semantics for non-unique keys. */
|
|
21
|
+
set<T>(_key: Key, _data: T, _codec?: Codec<T>): Promise<void>;
|
|
22
|
+
update(_key: Key, _data: Readonly<Record<string, unknown>>): Promise<void>;
|
|
23
|
+
delete(_key: Key): Promise<void>;
|
|
24
|
+
runReadwriteTransaction<Result>(callback: (transaction: ReadwriteTransaction) => Promise<Result>): Promise<Result>;
|
|
25
|
+
private tableFor;
|
|
26
|
+
private tableForCollection;
|
|
27
|
+
private fieldForColumn;
|
|
28
|
+
private selectForTable;
|
|
29
|
+
private snapshot;
|
|
30
|
+
private runQuery;
|
|
31
|
+
private allRows;
|
|
32
|
+
private request;
|
|
33
|
+
private withDeadline;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=database.d.ts.map
|