@keystrokehq/snowflake 0.0.16 → 0.0.18

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 (82) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -233
  3. package/package.json +39 -171
  4. package/dist/accounts/index.d.mts +0 -40
  5. package/dist/accounts/index.mjs +0 -51
  6. package/dist/bulk/index.d.mts +0 -71
  7. package/dist/bulk/index.mjs +0 -65
  8. package/dist/catalog/index.d.mts +0 -144
  9. package/dist/catalog/index.mjs +0 -158
  10. package/dist/client/index.d.mts +0 -44
  11. package/dist/client/index.mjs +0 -273
  12. package/dist/common-DfpCwJx5.mjs +0 -92
  13. package/dist/credential-sets/index.d.mts +0 -3
  14. package/dist/credential-sets/index.mjs +0 -4
  15. package/dist/databases/index.d.mts +0 -2
  16. package/dist/databases/index.mjs +0 -3
  17. package/dist/errors-60AsF7Su.mjs +0 -95
  18. package/dist/events/index.d.mts +0 -382
  19. package/dist/events/index.mjs +0 -291
  20. package/dist/file-formats/index.d.mts +0 -39
  21. package/dist/file-formats/index.mjs +0 -48
  22. package/dist/functions/index.d.mts +0 -53
  23. package/dist/functions/index.mjs +0 -56
  24. package/dist/grants/index.d.mts +0 -93
  25. package/dist/grants/index.mjs +0 -102
  26. package/dist/index.d.mts +0 -4
  27. package/dist/index.mjs +0 -6
  28. package/dist/operations/index.d.mts +0 -1
  29. package/dist/operations/index.mjs +0 -1
  30. package/dist/org-admin/index.d.mts +0 -80
  31. package/dist/org-admin/index.mjs +0 -79
  32. package/dist/pipes/index.d.mts +0 -119
  33. package/dist/pipes/index.mjs +0 -106
  34. package/dist/procedures/index.d.mts +0 -61
  35. package/dist/procedures/index.mjs +0 -79
  36. package/dist/results/index.d.mts +0 -16
  37. package/dist/results/index.mjs +0 -64
  38. package/dist/retry-B4-9MZIp.mjs +0 -164
  39. package/dist/retry-w7cTp1QL.d.mts +0 -10
  40. package/dist/roles/index.d.mts +0 -60
  41. package/dist/roles/index.mjs +0 -74
  42. package/dist/rows/index.d.mts +0 -106
  43. package/dist/rows/index.mjs +0 -223
  44. package/dist/schemas/index.d.mts +0 -2
  45. package/dist/schemas/index.mjs +0 -4
  46. package/dist/schemas-catalog/index.d.mts +0 -2
  47. package/dist/schemas-catalog/index.mjs +0 -3
  48. package/dist/shares/index.d.mts +0 -56
  49. package/dist/shares/index.mjs +0 -77
  50. package/dist/snowflake-s3-sns-webhook.credential-set-D6bLAWy5.d.mts +0 -50
  51. package/dist/snowflake-s3-sns-webhook.credential-set-DzR7oWNK.mjs +0 -44
  52. package/dist/snowflake.credential-set-BxT7Gf3A.d.mts +0 -114
  53. package/dist/snowflake.credential-set-DE3gXf-a.mjs +0 -106
  54. package/dist/sql/index.d.mts +0 -84
  55. package/dist/sql/index.mjs +0 -209
  56. package/dist/sql-options-2k5xQ-oS.d.mts +0 -32
  57. package/dist/sql-options-D6GEa21F.mjs +0 -79
  58. package/dist/sql-safety-BZpAPsq6.mjs +0 -56
  59. package/dist/stages/index.d.mts +0 -64
  60. package/dist/stages/index.mjs +0 -81
  61. package/dist/statements-BumhMhN4.mjs +0 -81
  62. package/dist/statements-DJL0qVNA.d.mts +0 -238
  63. package/dist/status-page/index.d.mts +0 -510
  64. package/dist/status-page/index.mjs +0 -261
  65. package/dist/streaming/index.d.mts +0 -70
  66. package/dist/streaming/index.mjs +0 -56
  67. package/dist/streams/index.d.mts +0 -71
  68. package/dist/streams/index.mjs +0 -78
  69. package/dist/tables/index.d.mts +0 -2
  70. package/dist/tables/index.mjs +0 -3
  71. package/dist/tasks/index.d.mts +0 -79
  72. package/dist/tasks/index.mjs +0 -104
  73. package/dist/triggers/index.d.mts +0 -381
  74. package/dist/triggers/index.mjs +0 -1100
  75. package/dist/users/index.d.mts +0 -61
  76. package/dist/users/index.mjs +0 -67
  77. package/dist/verification/index.d.mts +0 -201
  78. package/dist/verification/index.mjs +0 -512
  79. package/dist/views/index.d.mts +0 -2
  80. package/dist/views/index.mjs +0 -3
  81. package/dist/warehouses/index.d.mts +0 -68
  82. package/dist/warehouses/index.mjs +0 -101
@@ -1,512 +0,0 @@
1
- import { azureEventGridEventSchema, gcsPubSubPushSchema, snsNotificationSchema } from "../events/index.mjs";
2
- import { z } from "zod";
3
- import { X509Certificate, createPublicKey, createVerify } from "node:crypto";
4
-
5
- //#region src/verification/index.ts
6
- /**
7
- * Signature / authenticity verification helpers for the three
8
- * Snowflake auto-ingest webhook triggers. Everything here is
9
- * credential-free: each function is called from the webhook trigger
10
- * `verify` callback with request + expected-trust inputs extracted
11
- * from the relevant `CredentialSet`.
12
- *
13
- * All verifiers throw {@link SnowflakeWebhookVerificationError} on
14
- * failure. Callers should let the error propagate; Keystroke surfaces
15
- * it to the pipeline audit log and returns 4xx to the caller.
16
- */
17
- /**
18
- * Thrown whenever a webhook verifier rejects a request. Includes a
19
- * compact `reason` code so callers can distinguish transport problems
20
- * (signature mismatch) from policy problems (wrong audience).
21
- */
22
- var SnowflakeWebhookVerificationError = class extends Error {
23
- name = "SnowflakeWebhookVerificationError";
24
- reason;
25
- provider;
26
- constructor(input) {
27
- super(input.message, input.cause !== void 0 ? { cause: input.cause } : void 0);
28
- this.reason = input.reason;
29
- this.provider = input.provider ?? "unknown";
30
- }
31
- };
32
- function defaultFetch() {
33
- if (typeof fetch === "function") return fetch;
34
- throw new SnowflakeWebhookVerificationError({
35
- message: "global fetch is not available; pass a custom fetchImpl to the verifier.",
36
- reason: "no-fetch"
37
- });
38
- }
39
- function base64UrlDecode(value) {
40
- const pad = value.length % 4 === 0 ? "" : "=".repeat(4 - value.length % 4);
41
- return Buffer.from(value.replace(/-/gu, "+").replace(/_/gu, "/") + pad, "base64");
42
- }
43
- function readHeader(headers, name) {
44
- const lower = name.toLowerCase();
45
- for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === lower && typeof value === "string") return value;
46
- }
47
- const DEFAULT_SNS_HOST_PATTERN = /^sns\.[a-z0-9-]+\.amazonaws\.com(\.cn)?$/u;
48
- /**
49
- * Parse an SNS HTTP(S) notification body into a typed envelope. Throws
50
- * if the body is not valid JSON or doesn't match the SNS schema.
51
- */
52
- function parseSnsNotification(rawBody) {
53
- let json;
54
- try {
55
- json = JSON.parse(rawBody);
56
- } catch (cause) {
57
- throw new SnowflakeWebhookVerificationError({
58
- message: "SNS body is not valid JSON.",
59
- reason: "invalid-json",
60
- provider: "aws-s3",
61
- cause
62
- });
63
- }
64
- const parsed = snsNotificationSchema.safeParse(json);
65
- if (!parsed.success) throw new SnowflakeWebhookVerificationError({
66
- message: `SNS body failed schema validation: ${parsed.error.message}`,
67
- reason: "schema",
68
- provider: "aws-s3",
69
- cause: parsed.error
70
- });
71
- return parsed.data;
72
- }
73
- /**
74
- * Build the canonical string that SNS signs. The SNS spec fixes the
75
- * field order per message type — do not re-order keys.
76
- *
77
- * @see https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html
78
- */
79
- function canonicalSnsString(message) {
80
- const lines = [];
81
- if (message.Type === "Notification") {
82
- lines.push("Message", message.Message);
83
- lines.push("MessageId", message.MessageId);
84
- if (message.Subject !== void 0) lines.push("Subject", message.Subject);
85
- lines.push("Timestamp", message.Timestamp);
86
- lines.push("TopicArn", message.TopicArn);
87
- lines.push("Type", message.Type);
88
- } else {
89
- lines.push("Message", message.Message);
90
- lines.push("MessageId", message.MessageId);
91
- if (message.SubscribeURL !== void 0) lines.push("SubscribeURL", message.SubscribeURL);
92
- lines.push("Timestamp", message.Timestamp);
93
- if (message.Token !== void 0) lines.push("Token", message.Token);
94
- lines.push("TopicArn", message.TopicArn);
95
- lines.push("Type", message.Type);
96
- }
97
- return `${lines.join("\n")}\n`;
98
- }
99
- async function fetchSigningCertPem(signingCertUrl, options) {
100
- if (options.resolveCert) return options.resolveCert(signingCertUrl);
101
- const response = await (options.fetchImpl ?? defaultFetch())(signingCertUrl);
102
- if (!response.ok) throw new SnowflakeWebhookVerificationError({
103
- message: `Unable to fetch SNS signing certificate: ${response.status}`,
104
- reason: "cert-fetch",
105
- provider: "aws-s3"
106
- });
107
- return response.text();
108
- }
109
- /**
110
- * Verify an SNS notification. Performs:
111
- * 1. Topic-ARN pinning (if provided).
112
- * 2. SigningCertURL host allowlist (default: AWS SNS hosts only —
113
- * prevents SSRF against arbitrary URLs).
114
- * 3. Cert fetch + signature verification (SHA1 or SHA256 per
115
- * `SignatureVersion`).
116
- *
117
- * Returns the parsed notification on success.
118
- */
119
- async function verifySnsMessage(rawBody, options = {}) {
120
- const message = parseSnsNotification(rawBody);
121
- if (options.expectedTopicArn && message.TopicArn !== options.expectedTopicArn) throw new SnowflakeWebhookVerificationError({
122
- message: `SNS TopicArn mismatch: received ${message.TopicArn}, expected ${options.expectedTopicArn}.`,
123
- reason: "topic-arn-mismatch",
124
- provider: "aws-s3"
125
- });
126
- const hostPattern = options.allowedSigningCertHostPattern ? new RegExp(options.allowedSigningCertHostPattern, "u") : DEFAULT_SNS_HOST_PATTERN;
127
- let certUrl;
128
- try {
129
- certUrl = new URL(message.SigningCertURL);
130
- } catch (cause) {
131
- throw new SnowflakeWebhookVerificationError({
132
- message: "SNS SigningCertURL is not a valid URL.",
133
- reason: "cert-url-invalid",
134
- provider: "aws-s3",
135
- cause
136
- });
137
- }
138
- if (certUrl.protocol !== "https:" || !hostPattern.test(certUrl.hostname)) throw new SnowflakeWebhookVerificationError({
139
- message: `SNS SigningCertURL host ${certUrl.hostname} not in allowlist.`,
140
- reason: "cert-url-host",
141
- provider: "aws-s3"
142
- });
143
- const pem = await fetchSigningCertPem(message.SigningCertURL, options);
144
- let cert;
145
- try {
146
- cert = new X509Certificate(pem);
147
- } catch (cause) {
148
- throw new SnowflakeWebhookVerificationError({
149
- message: "SNS signing certificate is not a valid X.509 PEM.",
150
- reason: "cert-parse",
151
- provider: "aws-s3",
152
- cause
153
- });
154
- }
155
- const verifier = createVerify(message.SignatureVersion === "2" ? "RSA-SHA256" : "RSA-SHA1");
156
- verifier.update(canonicalSnsString(message), "utf8");
157
- if (!verifier.verify(cert.publicKey, message.Signature, "base64")) throw new SnowflakeWebhookVerificationError({
158
- message: "SNS signature did not verify against the signing certificate.",
159
- reason: "signature",
160
- provider: "aws-s3"
161
- });
162
- return message;
163
- }
164
- const googleJwksKeySchema = z.object({
165
- kid: z.string(),
166
- kty: z.string(),
167
- alg: z.string().optional(),
168
- use: z.string().optional(),
169
- n: z.string(),
170
- e: z.string()
171
- }).catchall(z.unknown());
172
- const googleJwksResponseSchema = z.object({ keys: z.array(googleJwksKeySchema) });
173
- const jwtHeaderSchema = z.object({
174
- alg: z.string(),
175
- kid: z.string().optional(),
176
- typ: z.string().optional()
177
- }).catchall(z.unknown());
178
- const googleOidcClaimsSchema = z.object({
179
- iss: z.string(),
180
- aud: z.union([z.string(), z.array(z.string())]).optional(),
181
- sub: z.string().optional(),
182
- email: z.string().optional(),
183
- email_verified: z.boolean().optional(),
184
- exp: z.number(),
185
- iat: z.number().optional(),
186
- nbf: z.number().optional()
187
- }).catchall(z.unknown());
188
- const jwksCache = /* @__PURE__ */ new Map();
189
- async function resolveJwksKeys(jwksUri, options) {
190
- if (options.resolveJwks) return options.resolveJwks(jwksUri);
191
- if (!options.disableCache) {
192
- const cached = jwksCache.get(jwksUri);
193
- const now = options.now ? options.now() : Date.now();
194
- if (cached && cached.expiresAt > now) return cached.keys;
195
- }
196
- const response = await (options.fetchImpl ?? defaultFetch())(jwksUri);
197
- if (!response.ok) throw new SnowflakeWebhookVerificationError({
198
- message: `Unable to fetch JWKS ${jwksUri}: ${response.status}`,
199
- reason: "jwks-fetch",
200
- provider: "gcp-gcs"
201
- });
202
- const json = await response.json();
203
- const parsed = googleJwksResponseSchema.safeParse(json);
204
- if (!parsed.success) throw new SnowflakeWebhookVerificationError({
205
- message: `JWKS payload failed schema validation: ${parsed.error.message}`,
206
- reason: "jwks-schema",
207
- provider: "gcp-gcs",
208
- cause: parsed.error
209
- });
210
- if (!options.disableCache) {
211
- const now = options.now ? options.now() : Date.now();
212
- jwksCache.set(jwksUri, {
213
- expiresAt: now + 36e5,
214
- keys: parsed.data.keys
215
- });
216
- }
217
- return parsed.data.keys;
218
- }
219
- /**
220
- * Verify the OIDC JWT that GCP Pub/Sub push sends in `Authorization:
221
- * Bearer <token>`. Returns the decoded claims on success.
222
- */
223
- async function verifyPubSubPushAuthorization(authorizationHeader, options) {
224
- if (!authorizationHeader) throw new SnowflakeWebhookVerificationError({
225
- message: "Missing Authorization header on Pub/Sub push request.",
226
- reason: "missing-auth-header",
227
- provider: "gcp-gcs"
228
- });
229
- const match = authorizationHeader.match(/^Bearer\s+(\S+)$/u);
230
- if (!match) throw new SnowflakeWebhookVerificationError({
231
- message: "Authorization header is not a Bearer token.",
232
- reason: "bad-auth-header",
233
- provider: "gcp-gcs"
234
- });
235
- const parts = (match[1] ?? "").split(".");
236
- if (parts.length !== 3) throw new SnowflakeWebhookVerificationError({
237
- message: "JWT must have three segments.",
238
- reason: "jwt-shape",
239
- provider: "gcp-gcs"
240
- });
241
- const [encodedHeader, encodedPayload, encodedSignature] = parts;
242
- let header;
243
- let claims;
244
- try {
245
- header = jwtHeaderSchema.parse(JSON.parse(base64UrlDecode(encodedHeader).toString("utf8")));
246
- claims = googleOidcClaimsSchema.parse(JSON.parse(base64UrlDecode(encodedPayload).toString("utf8")));
247
- } catch (cause) {
248
- throw new SnowflakeWebhookVerificationError({
249
- message: "JWT header or payload did not decode cleanly.",
250
- reason: "jwt-decode",
251
- provider: "gcp-gcs",
252
- cause
253
- });
254
- }
255
- if (header.alg !== "RS256") throw new SnowflakeWebhookVerificationError({
256
- message: `Unsupported JWT algorithm ${header.alg}; only RS256 is accepted.`,
257
- reason: "jwt-alg",
258
- provider: "gcp-gcs"
259
- });
260
- const keys = await resolveJwksKeys(options.jwksUri ?? "https://www.googleapis.com/oauth2/v3/certs", options);
261
- const jwk = header.kid ? keys.find((k) => k.kid === header.kid) : keys[0];
262
- if (!jwk) throw new SnowflakeWebhookVerificationError({
263
- message: `No JWKS key matched kid ${header.kid ?? "(none)"}.`,
264
- reason: "jwks-kid-miss",
265
- provider: "gcp-gcs"
266
- });
267
- const publicKey = createPublicKey({
268
- key: jwk,
269
- format: "jwk"
270
- });
271
- const verifier = createVerify("RSA-SHA256");
272
- verifier.update(`${encodedHeader}.${encodedPayload}`, "utf8");
273
- if (!verifier.verify(publicKey, base64UrlDecode(encodedSignature))) throw new SnowflakeWebhookVerificationError({
274
- message: "JWT signature did not verify.",
275
- reason: "jwt-signature",
276
- provider: "gcp-gcs"
277
- });
278
- const expectedIssuer = options.issuer ?? "https://accounts.google.com";
279
- if (claims.iss !== expectedIssuer) throw new SnowflakeWebhookVerificationError({
280
- message: `JWT iss ${claims.iss} != expected ${expectedIssuer}.`,
281
- reason: "jwt-issuer",
282
- provider: "gcp-gcs"
283
- });
284
- if (!(claims.aud === void 0 ? [] : Array.isArray(claims.aud) ? claims.aud : [claims.aud]).includes(options.expectedAudience)) throw new SnowflakeWebhookVerificationError({
285
- message: `JWT aud does not include ${options.expectedAudience}.`,
286
- reason: "jwt-audience",
287
- provider: "gcp-gcs"
288
- });
289
- if (claims.email !== options.expectedServiceAccount) throw new SnowflakeWebhookVerificationError({
290
- message: `JWT email ${claims.email} != expected service account ${options.expectedServiceAccount}.`,
291
- reason: "jwt-email",
292
- provider: "gcp-gcs"
293
- });
294
- const now = Math.floor((options.now ? options.now() : Date.now()) / 1e3);
295
- if (claims.exp < now) throw new SnowflakeWebhookVerificationError({
296
- message: "JWT is expired.",
297
- reason: "jwt-expired",
298
- provider: "gcp-gcs"
299
- });
300
- if (claims.nbf !== void 0 && claims.nbf > now + 60) throw new SnowflakeWebhookVerificationError({
301
- message: "JWT nbf is in the future.",
302
- reason: "jwt-nbf",
303
- provider: "gcp-gcs"
304
- });
305
- return claims;
306
- }
307
- /** Parse a Pub/Sub push envelope. Does not verify anything. */
308
- function parsePubSubPush(rawBody) {
309
- let json;
310
- try {
311
- json = JSON.parse(rawBody);
312
- } catch (cause) {
313
- throw new SnowflakeWebhookVerificationError({
314
- message: "Pub/Sub body is not valid JSON.",
315
- reason: "invalid-json",
316
- provider: "gcp-gcs",
317
- cause
318
- });
319
- }
320
- const parsed = gcsPubSubPushSchema.safeParse(json);
321
- if (!parsed.success) throw new SnowflakeWebhookVerificationError({
322
- message: `Pub/Sub body failed schema validation: ${parsed.error.message}`,
323
- reason: "schema",
324
- provider: "gcp-gcs",
325
- cause: parsed.error
326
- });
327
- return parsed.data;
328
- }
329
- /**
330
- * Event Grid sends its validation request as a single-event array with
331
- * `eventType: 'Microsoft.EventGrid.SubscriptionValidationEvent'`. The
332
- * Keystroke webhook primitive does not support dynamic responses, so
333
- * operators must use **manual validation** (VSCode extension, portal
334
- * click-through, or a curl to `validationUrl`) rather than relying on
335
- * synchronous `validationResponse`.
336
- */
337
- const EVENT_GRID_VALIDATION_EVENT_TYPE = "Microsoft.EventGrid.SubscriptionValidationEvent";
338
- /** Parse an Event Grid batch. Validates each envelope with Zod. */
339
- function parseEventGridBatch(rawBody) {
340
- let json;
341
- try {
342
- json = JSON.parse(rawBody);
343
- } catch (cause) {
344
- throw new SnowflakeWebhookVerificationError({
345
- message: "Event Grid body is not valid JSON.",
346
- reason: "invalid-json",
347
- provider: "azure-blob",
348
- cause
349
- });
350
- }
351
- if (!Array.isArray(json)) throw new SnowflakeWebhookVerificationError({
352
- message: "Event Grid body must be a JSON array.",
353
- reason: "shape",
354
- provider: "azure-blob"
355
- });
356
- const events = [];
357
- const validationEvents = [];
358
- for (const entry of json) {
359
- const parsed = azureEventGridEventSchema.safeParse(entry);
360
- if (!parsed.success) throw new SnowflakeWebhookVerificationError({
361
- message: `Event Grid event failed schema validation: ${parsed.error.message}`,
362
- reason: "schema",
363
- provider: "azure-blob",
364
- cause: parsed.error
365
- });
366
- const event = parsed.data;
367
- events.push(event);
368
- if (event.eventType === EVENT_GRID_VALIDATION_EVENT_TYPE) {
369
- const data = event.data;
370
- if (typeof data.validationCode === "string") validationEvents.push({
371
- event,
372
- data: {
373
- validationCode: data.validationCode,
374
- ...typeof data.validationUrl === "string" ? { validationUrl: data.validationUrl } : {}
375
- }
376
- });
377
- }
378
- }
379
- return {
380
- events,
381
- validationEvents
382
- };
383
- }
384
- /**
385
- * Verify an Event Grid request. Event Grid's trust model is
386
- * primarily the delivery handshake + network ACLs, but operators can
387
- * wire a shared secret into a custom header. This verifier:
388
- *
389
- * - Checks the shared secret (if configured on both ends).
390
- * - Parses + schema-validates the event batch.
391
- * - Optionally enforces a topic-ARN pin.
392
- */
393
- function verifyEventGridRequest(input, options = {}) {
394
- const batch = parseEventGridBatch(input.rawBody);
395
- if (options.sharedSecret) {
396
- const headerName = options.sharedSecretHeader ?? "aeg-subscription-secret";
397
- if (readHeader(input.headers, headerName) !== options.sharedSecret) throw new SnowflakeWebhookVerificationError({
398
- message: `Event Grid shared-secret header ${headerName} did not match.`,
399
- reason: "shared-secret",
400
- provider: "azure-blob"
401
- });
402
- }
403
- if (options.expectedTopic) {
404
- for (const event of batch.events) if (event.topic && event.topic !== options.expectedTopic) throw new SnowflakeWebhookVerificationError({
405
- message: `Event Grid topic ${event.topic} != expected ${options.expectedTopic}.`,
406
- reason: "topic",
407
- provider: "azure-blob"
408
- });
409
- }
410
- return batch;
411
- }
412
- const s3EventRecordSchema = z.object({
413
- eventTime: z.string().optional(),
414
- eventName: z.string().optional(),
415
- awsRegion: z.string().optional(),
416
- requestParameters: z.object({ sourceIPAddress: z.string().optional() }).catchall(z.unknown()).optional(),
417
- s3: z.object({
418
- bucket: z.object({ name: z.string() }).catchall(z.unknown()).optional(),
419
- object: z.object({
420
- key: z.string().optional(),
421
- size: z.number().optional(),
422
- eTag: z.string().optional(),
423
- sequencer: z.string().optional()
424
- }).catchall(z.unknown()).optional()
425
- }).catchall(z.unknown()).optional()
426
- }).catchall(z.unknown());
427
- const s3EventEnvelopeSchema = z.object({ Records: z.array(s3EventRecordSchema) }).catchall(z.unknown());
428
- /**
429
- * Extract S3 object events out of the inner {@link SnsNotification}
430
- * message body. SNS forwards the verbatim S3 notification JSON in
431
- * `Message`.
432
- */
433
- function extractS3Records(snsMessage) {
434
- if (snsMessage.Type !== "Notification") return [];
435
- let inner;
436
- try {
437
- inner = JSON.parse(snsMessage.Message);
438
- } catch {
439
- return [];
440
- }
441
- const parsed = s3EventEnvelopeSchema.safeParse(inner);
442
- if (!parsed.success) return [];
443
- return parsed.data.Records;
444
- }
445
- const gcsObjectPayloadSchema = z.object({
446
- bucket: z.string().optional(),
447
- name: z.string().optional(),
448
- size: z.union([z.string(), z.number()]).optional(),
449
- etag: z.string().optional(),
450
- generation: z.string().optional(),
451
- timeCreated: z.string().optional(),
452
- updated: z.string().optional()
453
- }).catchall(z.unknown());
454
- /**
455
- * Decode + validate the inner GCS object payload from a Pub/Sub push.
456
- * Returns `undefined` if the `message.data` field is missing or the
457
- * decoded body does not shape-match a GCS Object Change notification.
458
- */
459
- function extractGcsObjectPayload(push) {
460
- const encoded = push.message.data;
461
- if (typeof encoded !== "string" || encoded.length === 0) return void 0;
462
- let json;
463
- try {
464
- json = JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
465
- } catch {
466
- return;
467
- }
468
- const parsed = gcsObjectPayloadSchema.safeParse(json);
469
- return parsed.success ? parsed.data : void 0;
470
- }
471
- const azureBlobDataSchema = z.object({
472
- api: z.string().optional(),
473
- blobType: z.string().optional(),
474
- contentLength: z.number().optional(),
475
- contentType: z.string().optional(),
476
- eTag: z.string().optional(),
477
- url: z.string().optional(),
478
- sequencer: z.string().optional(),
479
- storageDiagnostics: z.unknown().optional()
480
- }).catchall(z.unknown());
481
- /**
482
- * Extract `bucket` + `key` from the `url` or `subject` fields of an
483
- * Event Grid blob event. Event Grid populates `subject` as
484
- * `/blobServices/default/containers/<container>/blobs/<key>` so we can
485
- * parse either.
486
- */
487
- function parseAzureBlobLocation(event) {
488
- const subject = event.subject ?? "";
489
- const match = subject.match(/\/containers\/([^/]+)\/blobs\/(.+)$/u);
490
- if (match?.[1] && match[2]) return {
491
- bucket: match[1],
492
- key: match[2]
493
- };
494
- const data = azureBlobDataSchema.safeParse(event.data);
495
- if (data.success && typeof data.data.url === "string") try {
496
- const parts = new URL(data.data.url).pathname.replace(/^\//u, "").split("/");
497
- if (parts.length >= 2) {
498
- const [container, ...rest] = parts;
499
- return {
500
- bucket: container ?? "",
501
- key: rest.join("/")
502
- };
503
- }
504
- } catch {}
505
- return {
506
- bucket: "",
507
- key: subject
508
- };
509
- }
510
-
511
- //#endregion
512
- export { EVENT_GRID_VALIDATION_EVENT_TYPE, SnowflakeWebhookVerificationError, canonicalSnsString, extractGcsObjectPayload, extractS3Records, parseAzureBlobLocation, parseEventGridBatch, parsePubSubPush, parseSnsNotification, verifyEventGridRequest, verifyPubSubPushAuthorization, verifySnsMessage };
@@ -1,2 +0,0 @@
1
- import { CatalogResult, ShowMaterializedViewsOptions, ShowViewsOptions, showMaterializedViews, showViews } from "../catalog/index.mjs";
2
- export { type CatalogResult, type ShowMaterializedViewsOptions, type ShowViewsOptions, showMaterializedViews, showViews };
@@ -1,3 +0,0 @@
1
- import { showMaterializedViews, showViews } from "../catalog/index.mjs";
2
-
3
- export { showMaterializedViews, showViews };
@@ -1,68 +0,0 @@
1
- import { SnowflakeClient } from "../client/index.mjs";
2
- import { d as HydratedRow } from "../statements-DJL0qVNA.mjs";
3
- import { t as OptionMap } from "../sql-options-2k5xQ-oS.mjs";
4
- import { CatalogResult, ShowOptions, showWarehouses } from "../catalog/index.mjs";
5
-
6
- //#region src/warehouses/index.d.ts
7
- interface WarehouseContext {
8
- readonly warehouse?: string;
9
- readonly role?: string;
10
- }
11
- interface CreateWarehouseOptions extends WarehouseContext {
12
- readonly name: string;
13
- readonly orReplace?: boolean;
14
- readonly ifNotExists?: boolean;
15
- /** `WAREHOUSE_SIZE = 'X-SMALL'`, `AUTO_SUSPEND = 60`, `AUTO_RESUME = TRUE`, etc. */
16
- readonly options?: OptionMap;
17
- readonly comment?: string;
18
- }
19
- declare function createWarehouse(client: SnowflakeClient, options: CreateWarehouseOptions): Promise<void>;
20
- interface AlterWarehouseOptions extends WarehouseContext {
21
- readonly name: string;
22
- readonly rename?: string;
23
- readonly set?: OptionMap;
24
- readonly unset?: readonly string[];
25
- readonly comment?: string;
26
- readonly ifExists?: boolean;
27
- }
28
- declare function alterWarehouse(client: SnowflakeClient, options: AlterWarehouseOptions): Promise<void>;
29
- interface DropWarehouseOptions extends WarehouseContext {
30
- readonly name: string;
31
- readonly ifExists?: boolean;
32
- }
33
- declare function dropWarehouse(client: SnowflakeClient, options: DropWarehouseOptions): Promise<void>;
34
- interface ResumeWarehouseOptions extends WarehouseContext {
35
- readonly name: string;
36
- readonly ifSuspended?: boolean;
37
- }
38
- declare function resumeWarehouse(client: SnowflakeClient, options: ResumeWarehouseOptions): Promise<void>;
39
- interface SuspendWarehouseOptions extends WarehouseContext {
40
- readonly name: string;
41
- }
42
- declare function suspendWarehouse(client: SnowflakeClient, options: SuspendWarehouseOptions): Promise<void>;
43
- interface DescribeWarehouseOptions extends WarehouseContext {
44
- readonly name: string;
45
- }
46
- declare function describeWarehouse(client: SnowflakeClient, options: DescribeWarehouseOptions): Promise<readonly HydratedRow[]>;
47
- interface UseWarehouseOptions extends WarehouseContext {
48
- readonly name: string;
49
- }
50
- declare function useWarehouse(client: SnowflakeClient, options: UseWarehouseOptions): Promise<void>;
51
- interface WarehouseHistoryOptions extends WarehouseContext {
52
- readonly warehouseName?: string;
53
- /** ISO-8601 timestamp (passed to `TO_TIMESTAMP_LTZ`). */
54
- readonly startTime?: string;
55
- readonly endTime?: string;
56
- }
57
- /**
58
- * `TABLE(INFORMATION_SCHEMA.WAREHOUSE_LOAD_HISTORY(...))` — queued / running
59
- * load metrics for a single warehouse.
60
- */
61
- declare function warehouseLoadHistory(client: SnowflakeClient, options?: WarehouseHistoryOptions): Promise<readonly HydratedRow[]>;
62
- /**
63
- * `TABLE(INFORMATION_SCHEMA.WAREHOUSE_METERING_HISTORY(...))` — credit usage
64
- * broken out by hour.
65
- */
66
- declare function warehouseMeteringHistory(client: SnowflakeClient, options?: WarehouseHistoryOptions): Promise<readonly HydratedRow[]>;
67
- //#endregion
68
- export { AlterWarehouseOptions, type CatalogResult, CreateWarehouseOptions, DescribeWarehouseOptions, DropWarehouseOptions, ResumeWarehouseOptions, type ShowOptions, SuspendWarehouseOptions, UseWarehouseOptions, WarehouseContext, WarehouseHistoryOptions, alterWarehouse, createWarehouse, describeWarehouse, dropWarehouse, resumeWarehouse, showWarehouses, suspendWarehouse, useWarehouse, warehouseLoadHistory, warehouseMeteringHistory };
@@ -1,101 +0,0 @@
1
- import { executeSql } from "../sql/index.mjs";
2
- import { a as quoteLiteral, i as quoteIdentifier } from "../sql-safety-BZpAPsq6.mjs";
3
- import { t as formatOptions } from "../sql-options-D6GEa21F.mjs";
4
- import { showWarehouses } from "../catalog/index.mjs";
5
-
6
- //#region src/warehouses/index.ts
7
- async function createWarehouse(client, options) {
8
- const prefix = `CREATE${options.orReplace ? " OR REPLACE" : ""} WAREHOUSE${options.ifNotExists ? " IF NOT EXISTS" : ""}`;
9
- const body = options.options && Object.keys(options.options).length > 0 ? ` ${formatOptions(options.options)}` : "";
10
- const comment = options.comment ? ` COMMENT = ${quoteLiteral(options.comment)}` : "";
11
- await executeSql(client, {
12
- statement: `${prefix} ${quoteIdentifier(options.name)}${body}${comment}`,
13
- warehouse: options.warehouse,
14
- role: options.role
15
- });
16
- }
17
- async function alterWarehouse(client, options) {
18
- let tail;
19
- if (options.rename) tail = `RENAME TO ${quoteIdentifier(options.rename)}`;
20
- else if (options.set && Object.keys(options.set).length > 0) tail = `SET ${formatOptions(options.set)}`;
21
- else if (options.unset && options.unset.length > 0) tail = `UNSET ${options.unset.map((k) => k.toUpperCase()).join(", ")}`;
22
- else if (options.comment !== void 0) tail = `SET COMMENT = ${quoteLiteral(options.comment)}`;
23
- else throw new Error("alterWarehouse requires one of: rename / set / unset / comment");
24
- await executeSql(client, {
25
- statement: `ALTER WAREHOUSE${options.ifExists ? " IF EXISTS" : ""} ${quoteIdentifier(options.name)} ${tail}`,
26
- warehouse: options.warehouse,
27
- role: options.role
28
- });
29
- }
30
- async function dropWarehouse(client, options) {
31
- await executeSql(client, {
32
- statement: `DROP WAREHOUSE${options.ifExists ? " IF EXISTS" : ""} ${quoteIdentifier(options.name)}`,
33
- warehouse: options.warehouse,
34
- role: options.role
35
- });
36
- }
37
- async function resumeWarehouse(client, options) {
38
- const suffix = options.ifSuspended ? " IF SUSPENDED" : "";
39
- await executeSql(client, {
40
- statement: `ALTER WAREHOUSE ${quoteIdentifier(options.name)} RESUME${suffix}`,
41
- warehouse: options.warehouse,
42
- role: options.role
43
- });
44
- }
45
- async function suspendWarehouse(client, options) {
46
- await executeSql(client, {
47
- statement: `ALTER WAREHOUSE ${quoteIdentifier(options.name)} SUSPEND`,
48
- warehouse: options.warehouse,
49
- role: options.role
50
- });
51
- }
52
- async function describeWarehouse(client, options) {
53
- return (await executeSql(client, {
54
- statement: `DESCRIBE WAREHOUSE ${quoteIdentifier(options.name)}`,
55
- warehouse: options.warehouse,
56
- role: options.role
57
- })).rows;
58
- }
59
- async function useWarehouse(client, options) {
60
- await executeSql(client, {
61
- statement: `USE WAREHOUSE ${quoteIdentifier(options.name)}`,
62
- warehouse: options.warehouse,
63
- role: options.role
64
- });
65
- }
66
- function renderTimestampArg(value) {
67
- return value ? `TO_TIMESTAMP_LTZ(${quoteLiteral(value)})` : "NULL";
68
- }
69
- /**
70
- * `TABLE(INFORMATION_SCHEMA.WAREHOUSE_LOAD_HISTORY(...))` — queued / running
71
- * load metrics for a single warehouse.
72
- */
73
- async function warehouseLoadHistory(client, options = {}) {
74
- const args = [];
75
- if (options.startTime !== void 0) args.push(`DATE_RANGE_START => ${renderTimestampArg(options.startTime)}`);
76
- if (options.endTime !== void 0) args.push(`DATE_RANGE_END => ${renderTimestampArg(options.endTime)}`);
77
- if (options.warehouseName) args.push(`WAREHOUSE_NAME => ${quoteLiteral(options.warehouseName)}`);
78
- return (await executeSql(client, {
79
- statement: `SELECT * FROM TABLE(INFORMATION_SCHEMA.WAREHOUSE_LOAD_HISTORY(${args.join(", ")}))`,
80
- warehouse: options.warehouse,
81
- role: options.role
82
- })).rows;
83
- }
84
- /**
85
- * `TABLE(INFORMATION_SCHEMA.WAREHOUSE_METERING_HISTORY(...))` — credit usage
86
- * broken out by hour.
87
- */
88
- async function warehouseMeteringHistory(client, options = {}) {
89
- const args = [];
90
- if (options.startTime !== void 0) args.push(`DATE_RANGE_START => ${renderTimestampArg(options.startTime)}`);
91
- if (options.endTime !== void 0) args.push(`DATE_RANGE_END => ${renderTimestampArg(options.endTime)}`);
92
- if (options.warehouseName) args.push(`WAREHOUSE_NAME => ${quoteLiteral(options.warehouseName)}`);
93
- return (await executeSql(client, {
94
- statement: `SELECT * FROM TABLE(INFORMATION_SCHEMA.WAREHOUSE_METERING_HISTORY(${args.join(", ")}))`,
95
- warehouse: options.warehouse,
96
- role: options.role
97
- })).rows;
98
- }
99
-
100
- //#endregion
101
- export { alterWarehouse, createWarehouse, describeWarehouse, dropWarehouse, resumeWarehouse, showWarehouses, suspendWarehouse, useWarehouse, warehouseLoadHistory, warehouseMeteringHistory };