@keystrokehq/snowflake 0.0.1 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Buster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,2 +1,2 @@
1
- import { c as snowflakeOfficialIntegration, i as snowflake, s as snowflakeBundle } from "../integration-CCWMSTlO.mjs";
1
+ import { c as snowflakeOfficialIntegration, i as snowflake, s as snowflakeBundle } from "../integration-CQNVr6NY.mjs";
2
2
  export { snowflake, snowflakeBundle, snowflakeOfficialIntegration };
@@ -1,3 +1,3 @@
1
- import { a as snowflakeBundle, n as snowflake, o as snowflakeOfficialIntegration } from "../integration-Dptv8L0L.mjs";
1
+ import { a as snowflakeBundle, n as snowflake, o as snowflakeOfficialIntegration } from "../integration-BVS1DWxb.mjs";
2
2
 
3
3
  export { snowflake, snowflakeBundle, snowflakeOfficialIntegration };
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as SnowflakeCredentials } from "./integration-CCWMSTlO.mjs";
1
+ import { n as SnowflakeCredentials } from "./integration-CQNVr6NY.mjs";
2
2
  import { t as RetryPolicy } from "./retry-w7cTp1QL.mjs";
3
3
 
4
4
  //#region src/client.d.ts
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as parseSnowflakeCredentials } from "./integration-Dptv8L0L.mjs";
1
+ import { t as parseSnowflakeCredentials } from "./integration-BVS1DWxb.mjs";
2
2
  import { a as extractSnowflakeMessage, c as parseRetryAfterHeader, i as extractSnowflakeCode, n as SnowflakeSqlError, o as extractSnowflakeSqlState, r as classifySnowflakeHttpStatus, s as extractSnowflakeStatementHandle, t as SnowflakeApiError } from "./errors-DKnc9o_a.mjs";
3
3
  import { a as createSnowflakeJwt, i as shouldRetry, n as computeBackoffMs, r as defaultSleep, t as DEFAULT_RETRY_POLICY } from "./retry-0hMfb8cC.mjs";
4
4
 
@@ -20,7 +20,7 @@ import { a as createSnowflakeJwt, i as shouldRetry, n as computeBackoffMs, r as
20
20
  */
21
21
  const DEFAULT_TIMEOUT_MS = 3e4;
22
22
  const JWT_REFRESH_SKEW_SECONDS = 60;
23
- const USER_AGENT = "keystroke-integration-snowflake/0.0.0";
23
+ const USER_AGENT = "keystroke-snowflake/0.0.0";
24
24
  function createSnowflakeClient(rawCredentials, options = {}) {
25
25
  const credentials = isSnowflakeCredentials(rawCredentials) ? rawCredentials : parseSnowflakeCredentials(rawCredentials);
26
26
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -1,2 +1,2 @@
1
- import { a as snowflakeAuthModeSchema, c as snowflakeOfficialIntegration, i as snowflake, n as SnowflakeCredentials, o as snowflakeAuthSchema, r as parseSnowflakeCredentials, s as snowflakeBundle, t as SnowflakeAuthMode } from "./integration-CCWMSTlO.mjs";
1
+ import { a as snowflakeAuthModeSchema, c as snowflakeOfficialIntegration, i as snowflake, n as SnowflakeCredentials, o as snowflakeAuthSchema, r as parseSnowflakeCredentials, s as snowflakeBundle, t as SnowflakeAuthMode } from "./integration-CQNVr6NY.mjs";
2
2
  export { type SnowflakeAuthMode, type SnowflakeCredentials, parseSnowflakeCredentials, snowflake, snowflakeAuthModeSchema, snowflakeAuthSchema, snowflakeBundle, snowflakeOfficialIntegration };
@@ -1,3 +1,3 @@
1
- import { a as snowflakeBundle, i as snowflakeAuthSchema, n as snowflake, o as snowflakeOfficialIntegration, r as snowflakeAuthModeSchema, t as parseSnowflakeCredentials } from "./integration-Dptv8L0L.mjs";
1
+ import { a as snowflakeBundle, i as snowflakeAuthSchema, n as snowflake, o as snowflakeOfficialIntegration, r as snowflakeAuthModeSchema, t as parseSnowflakeCredentials } from "./integration-BVS1DWxb.mjs";
2
2
 
3
3
  export { parseSnowflakeCredentials, snowflake, snowflakeAuthModeSchema, snowflakeAuthSchema, snowflakeBundle, snowflakeOfficialIntegration };
@@ -1,4 +1,4 @@
1
- import { defineOfficialIntegration } from "@keystrokehq/integration-authoring/official";
1
+ import { defineOfficialIntegration, officialCredentialSetId } from "@keystrokehq/integration-authoring/official";
2
2
  import { z } from "zod";
3
3
  import { CredentialSet } from "@keystrokehq/core";
4
4
 
@@ -28,7 +28,7 @@ import { CredentialSet } from "@keystrokehq/core";
28
28
  * certificate compromise) from posting events from an unrelated topic.
29
29
  */
30
30
  const snowflakeS3SnsWebhookCredentialSet = new CredentialSet({
31
- id: "snowflake-s3-sns-webhook",
31
+ id: officialCredentialSetId("snowflake-s3-sns-webhook"),
32
32
  exposure: "platform-only",
33
33
  name: "Snowflake Auto-Ingest — AWS SNS",
34
34
  auth: z.object({
@@ -51,7 +51,7 @@ const snowflakeS3SnsWebhookCredentialSet = new CredentialSet({
51
51
  * with any valid Google OIDC token from impersonating the subscription.
52
52
  */
53
53
  const snowflakeGcsPubSubWebhookCredentialSet = new CredentialSet({
54
- id: "snowflake-gcs-pubsub-webhook",
54
+ id: officialCredentialSetId("snowflake-gcs-pubsub-webhook"),
55
55
  exposure: "platform-only",
56
56
  name: "Snowflake Auto-Ingest — GCS Pub/Sub",
57
57
  auth: z.object({
@@ -74,7 +74,7 @@ const snowflakeGcsPubSubWebhookCredentialSet = new CredentialSet({
74
74
  * Cloud-Events schema. See `IMPLEMENTATION_NOTES.md` § trigger webhooks.
75
75
  */
76
76
  const snowflakeAzureEventGridWebhookCredentialSet = new CredentialSet({
77
- id: "snowflake-azure-eventgrid-webhook",
77
+ id: officialCredentialSetId("snowflake-azure-eventgrid-webhook"),
78
78
  exposure: "platform-only",
79
79
  name: "Snowflake Auto-Ingest — Azure Event Grid",
80
80
  auth: z.object({
@@ -204,4 +204,4 @@ function parseSnowflakeCredentials(input) {
204
204
  }
205
205
 
206
206
  //#endregion
207
- export { snowflakeBundle as a, snowflakeGcsPubSubWebhookCredentialSet as c, snowflakeAuthSchema as i, snowflakeS3SnsWebhookCredentialSet as l, snowflake as n, snowflakeOfficialIntegration as o, snowflakeAuthModeSchema as r, snowflakeAzureEventGridWebhookCredentialSet as s, parseSnowflakeCredentials as t };
207
+ export { snowflakeBundle as a, snowflakeAuthSchema as i, snowflake as n, snowflakeOfficialIntegration as o, snowflakeAuthModeSchema as r, parseSnowflakeCredentials as t };
@@ -106,7 +106,7 @@ declare const snowflakeBundle: _keystrokehq_integration_authoring_official0.Offi
106
106
  SNOWFLAKE_REGION: z.ZodOptional<z.ZodString>;
107
107
  SNOWFLAKE_PUBLIC_KEY_FP: z.ZodOptional<z.ZodString>;
108
108
  }, z.core.$strip>>;
109
- declare const snowflake: _keystrokehq_core0.CredentialSet<"snowflake", z.ZodObject<{
109
+ declare const snowflake: _keystrokehq_core0.CredentialSet<"keystroke:snowflake", z.ZodObject<{
110
110
  mode: z.ZodEnum<{
111
111
  keypair: "keypair";
112
112
  oauth: "oauth";
@@ -1,92 +1,16 @@
1
- import { i as snowflake } from "./integration-CCWMSTlO.mjs";
2
- import { scheduledDeltaEventSchema, scheduledSqlEventSchema, snowpipeObjectBatchSchema, streamAppendOnlyEventSchema, streamHasDataEventSchema } from "./events.mjs";
1
+ import { i as snowflake } from "./integration-CQNVr6NY.mjs";
2
+ import { scheduledDeltaEventSchema, scheduledSqlEventSchema, streamAppendOnlyEventSchema, streamHasDataEventSchema } from "./events.mjs";
3
3
  import { RowValue } from "./rows.mjs";
4
4
  import { StatusPageOptions } from "./status-page.mjs";
5
- import { VerifyEventGridRequestOptions, VerifyPubSubPushOptions, VerifySnsMessageOptions } from "./verification.mjs";
6
5
  import { z } from "zod";
7
- import * as _keystrokehq_core0 from "@keystrokehq/core";
8
- import { BoundTrigger, CredentialSet } from "@keystrokehq/core";
6
+ import { BoundTrigger } from "@keystrokehq/core";
9
7
  import { IntegrationTriggerBindingOptions, createPollingTriggerBindingFactory } from "@keystrokehq/integration-authoring";
10
- import * as _keystrokehq_core_credential_set0 from "@keystrokehq/core/credential-set";
11
- import { AnyCredentialSet, InferCredentialSetAuth } from "@keystrokehq/core/credential-set";
12
- import { PollingTriggerManifest, WebhookRequest, WebhookTriggerManifest } from "@keystrokehq/core/trigger";
8
+ import { PollingTriggerManifest } from "@keystrokehq/core/trigger";
13
9
 
14
- //#region src/webhook-credentials.d.ts
15
- /**
16
- * AWS SNS webhook credentials.
17
- *
18
- * SNS verifies message authenticity via an X.509 signature whose public
19
- * cert is published at an SNS-hosted URL. The only real secret is the
20
- * optional {@link expectedTopicArn}: tight-scoping to a single topic
21
- * prevents an attacker that fooled SNS signing (not possible absent a
22
- * certificate compromise) from posting events from an unrelated topic.
23
- */
24
- declare const snowflakeS3SnsWebhookCredentialSet: CredentialSet<"snowflake-s3-sns-webhook", z.ZodObject<{
25
- expectedTopicArn: z.ZodOptional<z.ZodString>;
26
- allowedSigningCertHostPattern: z.ZodOptional<z.ZodDefault<z.ZodString>>;
27
- autoConfirmSubscriptions: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
28
- true: "true";
29
- false: "false";
30
- }>>>;
31
- }, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
32
- expectedTopicArn: z.ZodOptional<z.ZodString>;
33
- allowedSigningCertHostPattern: z.ZodOptional<z.ZodDefault<z.ZodString>>;
34
- autoConfirmSubscriptions: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
35
- true: "true";
36
- false: "false";
37
- }>>>;
38
- }, z.core.$strip>>[] | undefined>;
39
- /**
40
- * GCP Pub/Sub push subscriptions sign each request with a Google-minted
41
- * OIDC JWT (RS256) in the `Authorization` header. Verification requires
42
- * knowing:
43
- *
44
- * - `expectedAudience`: the OIDC audience configured on the push
45
- * subscription (commonly the webhook URL itself).
46
- * - `expectedServiceAccount`: the push subscription's service-account
47
- * email, which appears as the JWT `email` claim.
48
- *
49
- * Both are public identifiers, but pinning them prevents an attacker
50
- * with any valid Google OIDC token from impersonating the subscription.
51
- */
52
- declare const snowflakeGcsPubSubWebhookCredentialSet: CredentialSet<"snowflake-gcs-pubsub-webhook", z.ZodObject<{
53
- expectedAudience: z.ZodString;
54
- expectedServiceAccount: z.ZodString;
55
- jwksUri: z.ZodOptional<z.ZodDefault<z.ZodString>>;
56
- issuer: z.ZodOptional<z.ZodDefault<z.ZodString>>;
57
- }, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
58
- expectedAudience: z.ZodString;
59
- expectedServiceAccount: z.ZodString;
60
- jwksUri: z.ZodOptional<z.ZodDefault<z.ZodString>>;
61
- issuer: z.ZodOptional<z.ZodDefault<z.ZodString>>;
62
- }, z.core.$strip>>[] | undefined>;
63
- /**
64
- * Azure Event Grid authenticates webhook endpoints via one-time
65
- * handshake validation (validation code or URL) rather than per-request
66
- * signatures. If the operator wires a **shared secret** into the
67
- * Event Grid subscription as a custom header, that secret can be
68
- * checked here as a defence-in-depth measure.
69
- *
70
- * Event Grid's synchronous handshake (`SubscriptionValidationEvent`) is
71
- * not fulfillable through the Keystroke static-response webhook
72
- * primitive; Snowflake operators should use **manual handshake** or
73
- * Cloud-Events schema. See `IMPLEMENTATION_NOTES.md` § trigger webhooks.
74
- */
75
- declare const snowflakeAzureEventGridWebhookCredentialSet: CredentialSet<"snowflake-azure-eventgrid-webhook", z.ZodObject<{
76
- sharedSecret: z.ZodOptional<z.ZodString>;
77
- sharedSecretHeader: z.ZodOptional<z.ZodDefault<z.ZodString>>;
78
- expectedTopicArn: z.ZodOptional<z.ZodString>;
79
- }, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
80
- sharedSecret: z.ZodOptional<z.ZodString>;
81
- sharedSecretHeader: z.ZodOptional<z.ZodDefault<z.ZodString>>;
82
- expectedTopicArn: z.ZodOptional<z.ZodString>;
83
- }, z.core.$strip>>[] | undefined>;
84
- //#endregion
85
10
  //#region src/triggers.d.ts
86
11
  type Schedule = Parameters<typeof createPollingTriggerBindingFactory>[0]['schedule'];
87
12
  type SnowflakeCredentialSets = readonly [typeof snowflake];
88
13
  type SnowflakePollingBoundTrigger<TResponse extends z.ZodTypeAny, TOutput = z.output<TResponse>> = BoundTrigger<z.output<TResponse>, SnowflakeCredentialSets, PollingTriggerManifest, TOutput>;
89
- type SnowflakeWebhookBoundTrigger<TPayloadSchema extends z.ZodTypeAny, TCredentialSets extends readonly [typeof snowflake, ...AnyCredentialSet[]], TOutput = SnowpipeObjectBatchOutput> = BoundTrigger<z.output<TPayloadSchema>, TCredentialSets, WebhookTriggerManifest, TOutput, WebhookRequest>;
90
14
  interface SnowflakePollingBindingOptions<TResponse extends z.ZodTypeAny, TOutput = z.output<TResponse>> extends IntegrationTriggerBindingOptions<z.output<TResponse>, TOutput> {
91
15
  readonly schedule?: Schedule;
92
16
  }
@@ -423,116 +347,10 @@ declare const statusPageComponentChangeBatchSchema: z.ZodObject<{
423
347
  * Status-page pollers don't need Snowflake credentials, but they still
424
348
  * flow through the Snowflake binding factory so they show up in the
425
349
  * polling manifest alongside the rest. Credentials are declared but
426
- * unused — the poll callback ignores `ctx.credentials.snowflake`.
350
+ * unused — the poll callback ignores `ctx.credentials['keystroke:snowflake']`.
427
351
  */
428
352
  declare function statusPageIncident<TOutput = z.output<typeof statusPageIncidentBatchSchema>>(input?: StatusPagePollingParams & SnowflakePollingBindingOptions<typeof statusPageIncidentBatchSchema, TOutput>): SnowflakePollingBoundTrigger<typeof statusPageIncidentBatchSchema, TOutput>;
429
353
  declare function statusPageComponentChanged<TOutput = z.output<typeof statusPageComponentChangeBatchSchema>>(input?: StatusPagePollingParams & SnowflakePollingBindingOptions<typeof statusPageComponentChangeBatchSchema, TOutput>): SnowflakePollingBoundTrigger<typeof statusPageComponentChangeBatchSchema, TOutput>;
430
- /**
431
- * Shape of the event emitted by an auto-ingest webhook trigger. Same
432
- * envelope across the 3 clouds so downstream pipelines can branch on
433
- * `provider` without re-modelling.
434
- */
435
- type SnowpipeObjectBatchOutput = z.output<typeof snowpipeObjectBatchSchema>;
436
- type SnowpipeWebhookRequest = _keystrokehq_core0.WebhookRequest;
437
- interface WebhookBindingOptions<TOutput = SnowpipeObjectBatchOutput> extends IntegrationTriggerBindingOptions<SnowpipeObjectBatchOutput, TOutput, SnowpipeWebhookRequest> {
438
- /**
439
- * Override the default `/snowflake/...` path suffix. The router serves
440
- * webhooks at `/api/v1/webhooks/{orgId}{path}` so authors only supply the
441
- * suffix that follows the organization id.
442
- */
443
- readonly path?: string;
444
- }
445
- /**
446
- * The public SNS payload schema accepted by this trigger. SNS delivers
447
- * an application/json body; we validate it before signature-checking.
448
- */
449
- declare const snsWebhookPayloadSchema: z.ZodObject<{
450
- Type: z.ZodEnum<{
451
- Notification: "Notification";
452
- SubscriptionConfirmation: "SubscriptionConfirmation";
453
- UnsubscribeConfirmation: "UnsubscribeConfirmation";
454
- }>;
455
- MessageId: z.ZodString;
456
- TopicArn: z.ZodString;
457
- Message: z.ZodString;
458
- Timestamp: z.ZodString;
459
- SignatureVersion: z.ZodString;
460
- Signature: z.ZodString;
461
- SigningCertURL: z.ZodString;
462
- Subject: z.ZodOptional<z.ZodString>;
463
- Token: z.ZodOptional<z.ZodString>;
464
- SubscribeURL: z.ZodOptional<z.ZodString>;
465
- UnsubscribeURL: z.ZodOptional<z.ZodString>;
466
- }, z.core.$catchall<z.ZodUnknown>>;
467
- /**
468
- * `triggers.autoIngestS3Sns` — receives AWS SNS HTTPS deliveries,
469
- * verifies the SNS signature against the signing certificate, and
470
- * emits normalized S3 object events. Subscription-confirmation
471
- * messages are surfaced via a sentinel event of type
472
- * `SubscriptionConfirmation` so operators can auto-confirm in
473
- * downstream logic.
474
- */
475
- declare function autoIngestS3Sns<TOutput = SnowpipeObjectBatchOutput>(input?: WebhookBindingOptions<TOutput> & {
476
- /** Test seam — injected verifier overrides. Not persisted. */readonly verifierOptions?: VerifySnsMessageOptions;
477
- }): SnowflakeWebhookBoundTrigger<typeof snsWebhookPayloadSchema, readonly [typeof snowflake, typeof snowflakeS3SnsWebhookCredentialSet], TOutput>;
478
- declare const gcsWebhookPayloadSchema: z.ZodObject<{
479
- message: z.ZodObject<{
480
- messageId: z.ZodOptional<z.ZodString>;
481
- publishTime: z.ZodOptional<z.ZodString>;
482
- data: z.ZodOptional<z.ZodString>;
483
- attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
484
- }, z.core.$catchall<z.ZodUnknown>>;
485
- subscription: z.ZodOptional<z.ZodString>;
486
- }, z.core.$catchall<z.ZodUnknown>>;
487
- /**
488
- * `triggers.autoIngestGcsPubSub` — receives GCP Pub/Sub push deliveries,
489
- * verifies the Google-signed OIDC JWT against Google's JWKS, and
490
- * emits normalized GCS object events extracted from the Pub/Sub
491
- * message body.
492
- */
493
- declare function autoIngestGcsPubSub<TOutput = SnowpipeObjectBatchOutput>(input?: WebhookBindingOptions<TOutput> & {
494
- readonly verifierOptions?: Partial<VerifyPubSubPushOptions>;
495
- }): SnowflakeWebhookBoundTrigger<typeof gcsWebhookPayloadSchema, readonly [typeof snowflake, typeof snowflakeGcsPubSubWebhookCredentialSet], TOutput>;
496
- /**
497
- * Event Grid wire format is `AzureEventGridEvent[]`. Keystroke's
498
- * webhook primitive validates a single payload schema — so we accept
499
- * either a single event or a wrapper object. The runtime reparses the
500
- * raw body to preserve the array shape.
501
- */
502
- declare const azureWebhookPayloadSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
503
- id: z.ZodString;
504
- eventType: z.ZodString;
505
- subject: z.ZodString;
506
- eventTime: z.ZodString;
507
- dataVersion: z.ZodOptional<z.ZodString>;
508
- metadataVersion: z.ZodOptional<z.ZodString>;
509
- topic: z.ZodOptional<z.ZodString>;
510
- data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
511
- }, z.core.$catchall<z.ZodUnknown>>>, z.ZodObject<{
512
- id: z.ZodString;
513
- eventType: z.ZodString;
514
- subject: z.ZodString;
515
- eventTime: z.ZodString;
516
- dataVersion: z.ZodOptional<z.ZodString>;
517
- metadataVersion: z.ZodOptional<z.ZodString>;
518
- topic: z.ZodOptional<z.ZodString>;
519
- data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
520
- }, z.core.$catchall<z.ZodUnknown>>]>;
521
- /**
522
- * `triggers.autoIngestAzureEventGrid` — receives Azure Event Grid
523
- * deliveries. Validates shared-secret header (if configured), parses
524
- * the batch, and emits normalized Azure Blob object events. The
525
- * synchronous subscription-validation handshake is NOT supported
526
- * through the Keystroke static-response primitive; operators must
527
- * use manual validation URLs (see IMPLEMENTATION_NOTES.md).
528
- */
529
- declare function autoIngestAzureEventGrid<TOutput = SnowpipeObjectBatchOutput>(input?: WebhookBindingOptions<TOutput> & {
530
- readonly verifierOptions?: VerifyEventGridRequestOptions;
531
- }): SnowflakeWebhookBoundTrigger<typeof azureWebhookPayloadSchema, readonly [typeof snowflake, typeof snowflakeAzureEventGridWebhookCredentialSet], TOutput>;
532
- /**
533
- * Convenience namespace grouping every polling trigger in this package.
534
- * Equivalent to calling the individual factory functions directly.
535
- */
536
354
  declare const polling: Readonly<{
537
355
  streamHasNewRows: typeof streamHasNewRows;
538
356
  streamAppendOnly: typeof streamAppendOnly;
@@ -557,22 +375,5 @@ declare const polling: Readonly<{
557
375
  statusPageIncident: typeof statusPageIncident;
558
376
  statusPageComponentChanged: typeof statusPageComponentChanged;
559
377
  }>;
560
- /**
561
- * Convenience namespace grouping every auto-ingest webhook trigger.
562
- */
563
- declare const webhooks: Readonly<{
564
- autoIngestS3Sns: typeof autoIngestS3Sns;
565
- autoIngestGcsPubSub: typeof autoIngestGcsPubSub;
566
- autoIngestAzureEventGrid: typeof autoIngestAzureEventGrid;
567
- }>;
568
- /**
569
- * Nested `autoIngest` namespace per the spec (§7 PLAN.md) so callers
570
- * can reach the three webhook triggers via `triggers.autoIngest.<cloud>`.
571
- */
572
- declare const autoIngest: Readonly<{
573
- s3Sns: typeof autoIngestS3Sns;
574
- gcsPubSub: typeof autoIngestGcsPubSub;
575
- azureEventGrid: typeof autoIngestAzureEventGrid;
576
- }>;
577
378
  //#endregion
578
- export { autoIngest, autoIngestAzureEventGrid, autoIngestGcsPubSub, autoIngestS3Sns, databaseCreated, databaseDropped, grantsChanged, pipeLoadCompleted, pipeLoadFailed, polling, roleCreated, roleDropped, scheduledDelta, scheduledSql, shareChanged, statementCompleted, statusPageComponentChanged, statusPageIncident, streamAppendOnly, streamHasNewRows, tableCreated, tableDropped, taskRunCompleted, userCreated, userDropped, warehouseCreditsUsage, warehouseQueueDepth, webhooks };
379
+ export { databaseCreated, databaseDropped, grantsChanged, pipeLoadCompleted, pipeLoadFailed, polling, roleCreated, roleDropped, scheduledDelta, scheduledSql, shareChanged, statementCompleted, statusPageComponentChanged, statusPageIncident, streamAppendOnly, streamHasNewRows, tableCreated, tableDropped, taskRunCompleted, userCreated, userDropped, warehouseCreditsUsage, warehouseQueueDepth };
package/dist/triggers.mjs CHANGED
@@ -1,13 +1,12 @@
1
- import { c as snowflakeGcsPubSubWebhookCredentialSet, l as snowflakeS3SnsWebhookCredentialSet, n as snowflake, s as snowflakeAzureEventGridWebhookCredentialSet } from "./integration-Dptv8L0L.mjs";
1
+ import { n as snowflake } from "./integration-BVS1DWxb.mjs";
2
2
  import { createSnowflakeClient } from "./client.mjs";
3
- import { azureEventGridEventSchema, gcsPubSubPushSchema, grantChangedEventSchema, pipeLoadEventSchema, scheduledDeltaEventSchema, scheduledSqlEventSchema, shareChangedEventSchema, snowflakeDiffSnapshotEntrySchema, snowflakeObjectLifecycleEventSchema, snsNotificationSchema, statementCompletedEventSchema, statusPageComponentChangeEventSchema, statusPageIncidentEventSchema, streamAppendOnlyEventSchema, streamHasDataEventSchema, taskRunEventSchema, warehouseCreditsEventSchema, warehouseQueueEventSchema } from "./events.mjs";
3
+ import { grantChangedEventSchema, pipeLoadEventSchema, scheduledDeltaEventSchema, scheduledSqlEventSchema, shareChangedEventSchema, snowflakeDiffSnapshotEntrySchema, snowflakeObjectLifecycleEventSchema, statementCompletedEventSchema, statusPageComponentChangeEventSchema, statusPageIncidentEventSchema, streamAppendOnlyEventSchema, streamHasDataEventSchema, taskRunEventSchema, warehouseCreditsEventSchema, warehouseQueueEventSchema } from "./events.mjs";
4
4
  import { executeSql } from "./sql.mjs";
5
5
  import { a as quoteLiteral, i as quoteIdentifier, t as buildFqn } from "./sql-safety-CywdR3EP.mjs";
6
6
  import { BindingBuilder } from "./rows.mjs";
7
7
  import { getStatusSummary, getUnresolvedIncidents } from "./status-page.mjs";
8
- import { EVENT_GRID_VALIDATION_EVENT_TYPE, SnowflakeWebhookVerificationError, extractGcsObjectPayload, extractS3Records, parseAzureBlobLocation, parsePubSubPush, verifyEventGridRequest, verifyPubSubPushAuthorization, verifySnsMessage } from "./verification.mjs";
9
8
  import { z } from "zod";
10
- import { createPollingTriggerBindingFactory, createWebhookTriggerBindingFactory } from "@keystrokehq/integration-authoring";
9
+ import { createPollingTriggerBindingFactory } from "@keystrokehq/integration-authoring";
11
10
 
12
11
  //#region src/triggers.ts
13
12
  function defineSnowflakePollingTrigger(definition) {
@@ -17,9 +16,8 @@ function splitBindingOptions(input) {
17
16
  return {
18
17
  schedule: input?.schedule,
19
18
  bindOptions: {
20
- ...input?.name ? { name: input.name } : {},
19
+ ...input?.id ? { id: input.id } : {},
21
20
  ...input?.description ? { description: input.description } : {},
22
- ...input?.filter ? { filter: input.filter } : {},
23
21
  ...input?.transform ? { transform: input.transform } : {}
24
22
  }
25
23
  };
@@ -30,17 +28,15 @@ function resolveSchedule(override, fallback) {
30
28
  }
31
29
  function createSnowflakePollingBindingFactory(definition, params, schedule) {
32
30
  return createPollingTriggerBindingFactory({
33
- defaultName: definition.defaultName,
31
+ defaultId: definition.defaultId,
34
32
  defaultDescription: definition.defaultDescription,
35
33
  schedule: resolveSchedule(schedule, definition.defaultSchedule),
36
34
  credentialSets: [snowflake],
37
35
  response: definition.responseSchema,
38
36
  poll: (ctx) => {
39
37
  const snowflakeCtx = ctx;
40
- return definition.poll(params, snowflakeCtx.credentials.snowflake, snowflakeCtx);
41
- },
42
- idempotencyKey: (response) => definition.idempotencyKey(response),
43
- definitionFilter: (response) => definition.hasPayload(response)
38
+ return definition.poll(params, snowflakeCtx.credentials["keystroke:snowflake"], snowflakeCtx);
39
+ }
44
40
  });
45
41
  }
46
42
  function bindSnowflakePollingTrigger(definition, params, input) {
@@ -73,7 +69,7 @@ function hashStatement(statement) {
73
69
  }
74
70
  const streamHasNewRowsDefinition = defineSnowflakePollingTrigger({
75
71
  responseSchema: streamHasDataEventSchema,
76
- defaultName: "Snowflake Stream Has New Rows",
72
+ defaultId: "snowflake.stream_has_new_rows",
77
73
  defaultDescription: "Fires when SYSTEM$STREAM_HAS_DATA returns TRUE for a Snowflake stream (fires at most once per schedule tick).",
78
74
  defaultSchedule: "1m",
79
75
  poll: async (params, credentials) => {
@@ -94,16 +90,14 @@ const streamHasNewRowsDefinition = defineSnowflakePollingTrigger({
94
90
  hasData,
95
91
  checkedAt: isoNow()
96
92
  };
97
- },
98
- idempotencyKey: (response) => `${response.streamName}:${response.checkedAt}`,
99
- hasPayload: (response) => response.hasData
93
+ }
100
94
  });
101
95
  function streamHasNewRows(input) {
102
96
  return bindSnowflakePollingTrigger(streamHasNewRowsDefinition, input, input);
103
97
  }
104
98
  const streamAppendOnlyDefinition = defineSnowflakePollingTrigger({
105
99
  responseSchema: streamAppendOnlyEventSchema,
106
- defaultName: "Snowflake Stream Append-Only",
100
+ defaultId: "snowflake.stream_append_only",
107
101
  defaultDescription: "Emits new inserted rows from a Snowflake stream on each poll. Consumers must progress the stream via downstream DML in a transaction.",
108
102
  defaultSchedule: "1m",
109
103
  poll: async (params, credentials) => {
@@ -123,16 +117,14 @@ const streamAppendOnlyDefinition = defineSnowflakePollingTrigger({
123
117
  rowCount: rows.length,
124
118
  fetchedAt: isoNow()
125
119
  };
126
- },
127
- idempotencyKey: (response) => `${response.streamName}:${response.fetchedAt}:${response.rowCount}`,
128
- hasPayload: (response) => response.rowCount > 0
120
+ }
129
121
  });
130
122
  function streamAppendOnly(input) {
131
123
  return bindSnowflakePollingTrigger(streamAppendOnlyDefinition, input, input);
132
124
  }
133
125
  const scheduledSqlDefinition = defineSnowflakePollingTrigger({
134
126
  responseSchema: scheduledSqlEventSchema,
135
- defaultName: "Snowflake Scheduled SQL",
127
+ defaultId: "snowflake.scheduled_sql",
136
128
  defaultDescription: "Runs a caller-supplied SQL statement on each schedule tick and emits the full result set.",
137
129
  defaultSchedule: "5m",
138
130
  poll: async (params, credentials) => {
@@ -159,15 +151,10 @@ const scheduledSqlDefinition = defineSnowflakePollingTrigger({
159
151
  rowCount: rows.length,
160
152
  fetchedAt: isoNow()
161
153
  };
162
- },
163
- idempotencyKey: (response) => `${response.statementDigest}:${response.fetchedAt}`,
164
- hasPayload: (response) => response.rowCount > 0
154
+ }
165
155
  });
166
156
  function scheduledSql(input) {
167
- return bindSnowflakePollingTrigger(input.emitOnEmpty ? {
168
- ...scheduledSqlDefinition,
169
- hasPayload: () => true
170
- } : scheduledSqlDefinition, input, input);
157
+ return bindSnowflakePollingTrigger(scheduledSqlDefinition, input, input);
171
158
  }
172
159
  function stringifyCursor(value) {
173
160
  if (value === null || value === void 0) return null;
@@ -177,7 +164,7 @@ function stringifyCursor(value) {
177
164
  }
178
165
  const scheduledDeltaDefinition = defineSnowflakePollingTrigger({
179
166
  responseSchema: scheduledDeltaEventSchema,
180
- defaultName: "Snowflake Scheduled Delta",
167
+ defaultId: "snowflake.scheduled_delta",
181
168
  defaultDescription: "Emits new rows from a Snowflake table where a cursor column exceeds the last observed high-water mark.",
182
169
  defaultSchedule: "1m",
183
170
  poll: async (params, credentials, ctx) => {
@@ -215,9 +202,7 @@ const scheduledDeltaDefinition = defineSnowflakePollingTrigger({
215
202
  rowCount: rows.length,
216
203
  fetchedAt: isoNow()
217
204
  };
218
- },
219
- idempotencyKey: (response) => `${response.table}:${response.cursorColumn}:${response.highWaterValue ?? "initial"}`,
220
- hasPayload: (response) => response.rowCount > 0
205
+ }
221
206
  });
222
207
  function scheduledDelta(input) {
223
208
  return bindSnowflakePollingTrigger(scheduledDeltaDefinition, input, input);
@@ -234,7 +219,7 @@ const taskRunCompletedDefinition = defineSnowflakePollingTrigger({
234
219
  highWaterValue: z.string().nullable(),
235
220
  fetchedAt: z.string()
236
221
  }).catchall(z.unknown()),
237
- defaultName: "Snowflake Task Run Completed",
222
+ defaultId: "snowflake.task_run_completed",
238
223
  defaultDescription: "Emits terminal task runs polled from INFORMATION_SCHEMA.TASK_HISTORY (SUCCEEDED / FAILED / CANCELLED).",
239
224
  defaultSchedule: "1m",
240
225
  poll: async (params, credentials, ctx) => {
@@ -272,9 +257,7 @@ const taskRunCompletedDefinition = defineSnowflakePollingTrigger({
272
257
  highWaterValue: readStringField(last ?? {}, "COMPLETED_TIME", "completed_time") ?? readStringField(last ?? {}, "SCHEDULED_TIME", "scheduled_time") ?? priorHighWater,
273
258
  fetchedAt: isoNow()
274
259
  };
275
- },
276
- idempotencyKey: (response) => `task-run:${response.highWaterValue ?? "initial"}:${response.events.length}`,
277
- hasPayload: (response) => response.events.length > 0
260
+ }
278
261
  });
279
262
  function taskRunCompleted(input) {
280
263
  return bindSnowflakePollingTrigger(taskRunCompletedDefinition, {
@@ -301,7 +284,7 @@ const statementCompletedDefinition = defineSnowflakePollingTrigger({
301
284
  highWaterValue: z.string().nullable(),
302
285
  fetchedAt: z.string()
303
286
  }).catchall(z.unknown()),
304
- defaultName: "Snowflake Statement Completed",
287
+ defaultId: "snowflake.statement_completed",
305
288
  defaultDescription: "Emits terminal statements from INFORMATION_SCHEMA.QUERY_HISTORY, optionally filtered by user / text / warehouse.",
306
289
  defaultSchedule: "1m",
307
290
  poll: async (params, credentials, ctx) => {
@@ -349,9 +332,7 @@ const statementCompletedDefinition = defineSnowflakePollingTrigger({
349
332
  highWaterValue: readStringField(last ?? {}, "END_TIME", "end_time") ?? readStringField(last ?? {}, "START_TIME", "start_time") ?? priorHighWater,
350
333
  fetchedAt: isoNow()
351
334
  };
352
- },
353
- idempotencyKey: (response) => `stmt:${response.highWaterValue ?? "initial"}:${response.events.length}`,
354
- hasPayload: (response) => response.events.length > 0
335
+ }
355
336
  });
356
337
  function statementCompleted(input) {
357
338
  return bindSnowflakePollingTrigger(statementCompletedDefinition, {
@@ -372,10 +353,10 @@ const pipeLoadBatchSchema = z.object({
372
353
  highWaterValue: z.string().nullable(),
373
354
  fetchedAt: z.string()
374
355
  }).catchall(z.unknown());
375
- function buildPipeLoadDefinition(defaultName, defaultDescription, statusPredicate) {
356
+ function buildPipeLoadDefinition(defaultId, defaultDescription, statusPredicate) {
376
357
  return defineSnowflakePollingTrigger({
377
358
  responseSchema: pipeLoadBatchSchema,
378
- defaultName,
359
+ defaultId,
379
360
  defaultDescription,
380
361
  defaultSchedule: "1m",
381
362
  poll: async (params, credentials, ctx) => {
@@ -418,13 +399,11 @@ function buildPipeLoadDefinition(defaultName, defaultDescription, statusPredicat
418
399
  highWaterValue: readStringField(last ?? {}, "LAST_LOAD_TIME", "last_load_time") ?? priorHighWater,
419
400
  fetchedAt: isoNow()
420
401
  };
421
- },
422
- idempotencyKey: (response) => `pipe-load:${response.highWaterValue ?? "initial"}:${response.events.length}`,
423
- hasPayload: (response) => response.events.length > 0
402
+ }
424
403
  });
425
404
  }
426
- const pipeLoadCompletedDefinition = buildPipeLoadDefinition("Snowflake Pipe Load Completed", "Emits successful Snowpipe loads polled from INFORMATION_SCHEMA.COPY_HISTORY.", `STATUS = 'LOADED'`);
427
- const pipeLoadFailedDefinition = buildPipeLoadDefinition("Snowflake Pipe Load Failed", "Emits failed Snowpipe loads polled from INFORMATION_SCHEMA.COPY_HISTORY.", `STATUS IN ('LOAD_FAILED', 'PARTIALLY_LOADED')`);
405
+ const pipeLoadCompletedDefinition = buildPipeLoadDefinition("snowflake.pipe_load_completed", "Emits successful Snowpipe loads polled from INFORMATION_SCHEMA.COPY_HISTORY.", `STATUS = 'LOADED'`);
406
+ const pipeLoadFailedDefinition = buildPipeLoadDefinition("snowflake.pipe_load_failed", "Emits failed Snowpipe loads polled from INFORMATION_SCHEMA.COPY_HISTORY.", `STATUS IN ('LOAD_FAILED', 'PARTIALLY_LOADED')`);
428
407
  function pipeLoadCompleted(input) {
429
408
  return bindSnowflakePollingTrigger(pipeLoadCompletedDefinition, {
430
409
  pipeName: input?.pipeName,
@@ -493,9 +472,6 @@ function toSnapshotEntries(entries) {
493
472
  ...e.signature !== void 0 ? { signature: e.signature } : {}
494
473
  }));
495
474
  }
496
- function sortedIdempotencyKey(prefix, events, getIdentity = (e) => e.identity ?? "") {
497
- return `${prefix}:${events.map(getIdentity).slice().sort().join(",") || "empty"}`;
498
- }
499
475
  const lifecycleBatchSchema = buildDiffBatchSchema(snowflakeObjectLifecycleEventSchema);
500
476
  function rowToLifecycleEntry(kind, row) {
501
477
  const name = readStringField(row, "name", "NAME") ?? "";
@@ -527,7 +503,7 @@ function lifecycleEventFromEntry(kind, change, entry, detectedAt) {
527
503
  function buildLifecycleDefinition(config) {
528
504
  return defineSnowflakePollingTrigger({
529
505
  responseSchema: lifecycleBatchSchema,
530
- defaultName: config.defaultName,
506
+ defaultId: config.defaultId,
531
507
  defaultDescription: config.defaultDescription,
532
508
  defaultSchedule: "5m",
533
509
  poll: async (params, credentials, ctx) => {
@@ -548,9 +524,7 @@ function buildLifecycleDefinition(config) {
548
524
  snapshot: toSnapshotEntries(current),
549
525
  fetchedAt: detectedAt
550
526
  };
551
- },
552
- idempotencyKey: (response) => sortedIdempotencyKey(`${config.kind}:${config.change}`, response.events),
553
- hasPayload: (response) => response.events.length > 0
527
+ }
554
528
  });
555
529
  }
556
530
  function likeClause(like) {
@@ -566,14 +540,14 @@ function inScope(params) {
566
540
  const userCreatedDefinition = buildLifecycleDefinition({
567
541
  kind: "USER",
568
542
  change: "created",
569
- defaultName: "Snowflake User Created",
543
+ defaultId: "snowflake.user_created",
570
544
  defaultDescription: "Emits every new user observed via SHOW USERS since the prior poll.",
571
545
  buildStatement: (params) => `SHOW USERS${likeClause(params.like)}`
572
546
  });
573
547
  const userDroppedDefinition = buildLifecycleDefinition({
574
548
  kind: "USER",
575
549
  change: "dropped",
576
- defaultName: "Snowflake User Dropped",
550
+ defaultId: "snowflake.user_dropped",
577
551
  defaultDescription: "Emits every user that has disappeared from SHOW USERS since the prior poll.",
578
552
  buildStatement: (params) => `SHOW USERS${likeClause(params.like)}`
579
553
  });
@@ -599,14 +573,14 @@ function userDropped(input) {
599
573
  const roleCreatedDefinition = buildLifecycleDefinition({
600
574
  kind: "ROLE",
601
575
  change: "created",
602
- defaultName: "Snowflake Role Created",
576
+ defaultId: "snowflake.role_created",
603
577
  defaultDescription: "Emits every new role observed via SHOW ROLES since the prior poll.",
604
578
  buildStatement: (params) => `SHOW ROLES${likeClause(params.like)}`
605
579
  });
606
580
  const roleDroppedDefinition = buildLifecycleDefinition({
607
581
  kind: "ROLE",
608
582
  change: "dropped",
609
- defaultName: "Snowflake Role Dropped",
583
+ defaultId: "snowflake.role_dropped",
610
584
  defaultDescription: "Emits every role that has disappeared from SHOW ROLES since the prior poll.",
611
585
  buildStatement: (params) => `SHOW ROLES${likeClause(params.like)}`
612
586
  });
@@ -628,14 +602,14 @@ function roleDropped(input) {
628
602
  const databaseCreatedDefinition = buildLifecycleDefinition({
629
603
  kind: "DATABASE",
630
604
  change: "created",
631
- defaultName: "Snowflake Database Created",
605
+ defaultId: "snowflake.database_created",
632
606
  defaultDescription: "Emits every new database observed via SHOW DATABASES since the prior poll.",
633
607
  buildStatement: (params) => `SHOW DATABASES${likeClause(params.like)}`
634
608
  });
635
609
  const databaseDroppedDefinition = buildLifecycleDefinition({
636
610
  kind: "DATABASE",
637
611
  change: "dropped",
638
- defaultName: "Snowflake Database Dropped",
612
+ defaultId: "snowflake.database_dropped",
639
613
  defaultDescription: "Emits every database that disappeared from SHOW DATABASES since the prior poll.",
640
614
  buildStatement: (params) => `SHOW DATABASES${likeClause(params.like)}`
641
615
  });
@@ -657,14 +631,14 @@ function databaseDropped(input) {
657
631
  const tableCreatedDefinition = buildLifecycleDefinition({
658
632
  kind: "TABLE",
659
633
  change: "created",
660
- defaultName: "Snowflake Table Created",
634
+ defaultId: "snowflake.table_created",
661
635
  defaultDescription: "Emits every new table observed via SHOW TABLES since the prior poll.",
662
636
  buildStatement: (params) => `SHOW TABLES${likeClause(params.like)}${inScope(params)}`
663
637
  });
664
638
  const tableDroppedDefinition = buildLifecycleDefinition({
665
639
  kind: "TABLE",
666
640
  change: "dropped",
667
- defaultName: "Snowflake Table Dropped",
641
+ defaultId: "snowflake.table_dropped",
668
642
  defaultDescription: "Emits every table that disappeared from SHOW TABLES since the prior poll.",
669
643
  buildStatement: (params) => `SHOW TABLES${likeClause(params.like)}${inScope(params)}`
670
644
  });
@@ -707,7 +681,7 @@ function signatureOf(row, fields) {
707
681
  }
708
682
  const shareChangedDefinition = defineSnowflakePollingTrigger({
709
683
  responseSchema: shareChangedBatchSchema,
710
- defaultName: "Snowflake Share Changed",
684
+ defaultId: "snowflake.share_changed",
711
685
  defaultDescription: "Emits share create / drop / modify events observed via SHOW SHARES. Modifications diff against a signature of kind / owner / comment / accounts / listing.",
712
686
  defaultSchedule: "5m",
713
687
  poll: async (params, credentials, ctx) => {
@@ -764,9 +738,7 @@ const shareChangedDefinition = defineSnowflakePollingTrigger({
764
738
  snapshot: toSnapshotEntries(current),
765
739
  fetchedAt: detectedAt
766
740
  };
767
- },
768
- idempotencyKey: (response) => sortedIdempotencyKey("share-changed", response.events),
769
- hasPayload: (response) => response.events.length > 0
741
+ }
770
742
  });
771
743
  function shareChanged(input) {
772
744
  return bindSnowflakePollingTrigger(shareChangedDefinition, {
@@ -787,7 +759,7 @@ function grantStatement(params) {
787
759
  }
788
760
  const grantsChangedDefinition = defineSnowflakePollingTrigger({
789
761
  responseSchema: grantsChangedBatchSchema,
790
- defaultName: "Snowflake Grants Changed",
762
+ defaultId: "snowflake.grants_changed",
791
763
  defaultDescription: "Emits grant / revoke events observed via SHOW GRANTS TO <ROLE|USER|SHARE>. Diffs against the prior poll by (privilege, granted_on, name, grantee).",
792
764
  defaultSchedule: "5m",
793
765
  poll: async (params, credentials, ctx) => {
@@ -826,9 +798,7 @@ const grantsChangedDefinition = defineSnowflakePollingTrigger({
826
798
  snapshot: toSnapshotEntries(current),
827
799
  fetchedAt: detectedAt
828
800
  };
829
- },
830
- idempotencyKey: (response) => sortedIdempotencyKey("grants-changed", response.events),
831
- hasPayload: (response) => response.events.length > 0
801
+ }
832
802
  });
833
803
  function buildGrantEvent(row, change, identity, detectedAt) {
834
804
  return {
@@ -858,7 +828,7 @@ const warehouseQueueBatchSchema = z.object({
858
828
  }).catchall(z.unknown());
859
829
  const warehouseCreditsUsageDefinition = defineSnowflakePollingTrigger({
860
830
  responseSchema: warehouseCreditsBatchSchema,
861
- defaultName: "Snowflake Warehouse Credits Usage",
831
+ defaultId: "snowflake.warehouse_credits_usage",
862
832
  defaultDescription: "Tails INFORMATION_SCHEMA.WAREHOUSE_METERING_HISTORY and emits per-warehouse credit buckets above an optional minimum.",
863
833
  defaultSchedule: "5m",
864
834
  poll: async (params, credentials, ctx) => {
@@ -893,13 +863,11 @@ const warehouseCreditsUsageDefinition = defineSnowflakePollingTrigger({
893
863
  highWaterValue: readStringField(last ?? {}, "END_TIME", "end_time") ?? priorHighWater,
894
864
  fetchedAt: isoNow()
895
865
  };
896
- },
897
- idempotencyKey: (response) => `wh-credits:${response.highWaterValue ?? "initial"}:${response.events.length}`,
898
- hasPayload: (response) => response.events.length > 0
866
+ }
899
867
  });
900
868
  const warehouseQueueDepthDefinition = defineSnowflakePollingTrigger({
901
869
  responseSchema: warehouseQueueBatchSchema,
902
- defaultName: "Snowflake Warehouse Queue Depth",
870
+ defaultId: "snowflake.warehouse_queue_depth",
903
871
  defaultDescription: "Tails INFORMATION_SCHEMA.WAREHOUSE_LOAD_HISTORY and emits per-interval queue depth above an optional minimum.",
904
872
  defaultSchedule: "5m",
905
873
  poll: async (params, credentials, ctx) => {
@@ -935,9 +903,7 @@ const warehouseQueueDepthDefinition = defineSnowflakePollingTrigger({
935
903
  highWaterValue: readStringField(last ?? {}, "END_TIME", "end_time") ?? priorHighWater,
936
904
  fetchedAt: isoNow()
937
905
  };
938
- },
939
- idempotencyKey: (response) => `wh-queue:${response.highWaterValue ?? "initial"}:${response.events.length}`,
940
- hasPayload: (response) => response.events.length > 0
906
+ }
941
907
  });
942
908
  function warehouseCreditsUsage(input) {
943
909
  return bindSnowflakePollingTrigger(warehouseCreditsUsageDefinition, input ?? {}, input);
@@ -974,7 +940,7 @@ function extractStatusPageOptions(params) {
974
940
  }
975
941
  const statusPageIncidentDefinition = {
976
942
  responseSchema: statusPageIncidentBatchSchema,
977
- defaultName: "Snowflake Status Page Incident",
943
+ defaultId: "snowflake.status_page_incident",
978
944
  defaultDescription: "Polls https://status.snowflake.com/api/v2/incidents/unresolved.json and emits newly-appearing or status-changed incidents.",
979
945
  defaultSchedule: "5m",
980
946
  poll: async (params, _credentials, ctx) => {
@@ -1007,13 +973,11 @@ const statusPageIncidentDefinition = {
1007
973
  snapshot: toSnapshotEntries(current),
1008
974
  fetchedAt: detectedAt
1009
975
  };
1010
- },
1011
- idempotencyKey: (response) => sortedIdempotencyKey("status-incident", response.events, (e) => `${e.id}:${e.updatedAt ?? ""}`),
1012
- hasPayload: (response) => response.events.length > 0
976
+ }
1013
977
  };
1014
978
  const statusPageComponentChangeDefinition = {
1015
979
  responseSchema: statusPageComponentChangeBatchSchema,
1016
- defaultName: "Snowflake Status Page Component Changed",
980
+ defaultId: "snowflake.status_page_component_changed",
1017
981
  defaultDescription: "Polls https://status.snowflake.com/api/v2/summary.json and emits a change event when any component transitions status.",
1018
982
  defaultSchedule: "2m",
1019
983
  poll: async (params, _credentials, ctx) => {
@@ -1045,15 +1009,13 @@ const statusPageComponentChangeDefinition = {
1045
1009
  snapshot: toSnapshotEntries(current),
1046
1010
  fetchedAt: detectedAt
1047
1011
  };
1048
- },
1049
- idempotencyKey: (response) => sortedIdempotencyKey("status-component", response.events, (e) => `${e.id}:${e.currentStatus}`),
1050
- hasPayload: (response) => response.events.length > 0
1012
+ }
1051
1013
  };
1052
1014
  /**
1053
1015
  * Status-page pollers don't need Snowflake credentials, but they still
1054
1016
  * flow through the Snowflake binding factory so they show up in the
1055
1017
  * polling manifest alongside the rest. Credentials are declared but
1056
- * unused — the poll callback ignores `ctx.credentials.snowflake`.
1018
+ * unused — the poll callback ignores `ctx.credentials['keystroke:snowflake']`.
1057
1019
  */
1058
1020
  function statusPageIncident(input) {
1059
1021
  return bindSnowflakePollingTrigger(statusPageIncidentDefinition, input ?? {}, input);
@@ -1061,262 +1023,6 @@ function statusPageIncident(input) {
1061
1023
  function statusPageComponentChanged(input) {
1062
1024
  return bindSnowflakePollingTrigger(statusPageComponentChangeDefinition, input ?? {}, input);
1063
1025
  }
1064
- function resolveWebhookPath(override, fallback) {
1065
- return override ?? fallback;
1066
- }
1067
- function isoNowUtc() {
1068
- return (/* @__PURE__ */ new Date()).toISOString();
1069
- }
1070
- /**
1071
- * The public SNS payload schema accepted by this trigger. SNS delivers
1072
- * an application/json body; we validate it before signature-checking.
1073
- */
1074
- const snsWebhookPayloadSchema = snsNotificationSchema;
1075
- function s3RecordsToBatch(snsMessage, rawBody) {
1076
- const receivedAt = isoNowUtc();
1077
- return {
1078
- events: extractS3Records(snsMessage).map((record) => {
1079
- const bucket = record.s3?.bucket?.name ?? "";
1080
- const key = record.s3?.object?.key ?? "";
1081
- return {
1082
- provider: "aws-s3",
1083
- eventType: record.eventName ?? "",
1084
- eventTime: record.eventTime ?? receivedAt,
1085
- bucket,
1086
- key,
1087
- size: typeof record.s3?.object?.size === "number" ? record.s3.object.size : null,
1088
- etag: record.s3?.object?.eTag ?? null,
1089
- region: record.awsRegion ?? null,
1090
- sourceIp: record.requestParameters?.sourceIPAddress ?? null,
1091
- messageId: snsMessage.MessageId,
1092
- receivedAt,
1093
- raw: record
1094
- };
1095
- }),
1096
- provider: "aws-s3",
1097
- receivedAt,
1098
- messageId: snsMessage.MessageId,
1099
- topic: snsMessage.TopicArn,
1100
- raw: JSON.parse(rawBody)
1101
- };
1102
- }
1103
- /**
1104
- * `triggers.autoIngestS3Sns` — receives AWS SNS HTTPS deliveries,
1105
- * verifies the SNS signature against the signing certificate, and
1106
- * emits normalized S3 object events. Subscription-confirmation
1107
- * messages are surfaced via a sentinel event of type
1108
- * `SubscriptionConfirmation` so operators can auto-confirm in
1109
- * downstream logic.
1110
- */
1111
- function autoIngestS3Sns(input) {
1112
- const { verifierOptions, path, ...rest } = input ?? {};
1113
- return createWebhookTriggerBindingFactory({
1114
- defaultName: "Snowflake Auto-Ingest — AWS SNS",
1115
- defaultDescription: "Receives Snowpipe auto-ingest notifications via AWS SNS HTTPS delivery. Verifies SNS signature against the SigningCertURL, pins TopicArn, and normalizes S3 object events.",
1116
- path: resolveWebhookPath(path, "/snowflake/auto-ingest/s3-sns"),
1117
- method: "POST",
1118
- payload: snsWebhookPayloadSchema,
1119
- credentialSets: [snowflake, snowflakeS3SnsWebhookCredentialSet],
1120
- response: { successStatus: 200 },
1121
- verify: async (request, ctx) => {
1122
- const creds = ctx.credentials["snowflake-s3-sns-webhook"];
1123
- try {
1124
- await verifySnsMessage(request.rawBody, {
1125
- ...creds.expectedTopicArn ? { expectedTopicArn: creds.expectedTopicArn } : {},
1126
- ...creds.allowedSigningCertHostPattern ? { allowedSigningCertHostPattern: creds.allowedSigningCertHostPattern } : {},
1127
- ...verifierOptions ?? {}
1128
- });
1129
- } catch (err) {
1130
- if (err instanceof SnowflakeWebhookVerificationError) throw err;
1131
- throw new SnowflakeWebhookVerificationError({
1132
- message: "Unexpected error verifying SNS signature.",
1133
- reason: "unknown",
1134
- provider: "aws-s3",
1135
- cause: err
1136
- });
1137
- }
1138
- },
1139
- mapPayload: (payload) => {
1140
- if (payload.Type !== "Notification") return null;
1141
- const batch = s3RecordsToBatch(payload, JSON.stringify(payload));
1142
- return batch.events.length > 0 ? batch : null;
1143
- },
1144
- idempotencyKey: (_payload, mapped) => mapped ? `sns:${mapped.messageId ?? "no-id"}` : void 0
1145
- })(rest);
1146
- }
1147
- const gcsWebhookPayloadSchema = gcsPubSubPushSchema;
1148
- function gcsPushToBatch(push) {
1149
- const receivedAt = isoNowUtc();
1150
- const payload = extractGcsObjectPayload(push);
1151
- const attributes = push.message.attributes ?? {};
1152
- if (!payload || !payload.bucket || !payload.name) return {
1153
- events: [],
1154
- provider: "gcp-gcs",
1155
- receivedAt,
1156
- messageId: push.message.messageId ?? null,
1157
- topic: push.subscription ?? null,
1158
- raw: push
1159
- };
1160
- const eventType = attributes.eventType ?? "OBJECT_FINALIZE";
1161
- const sizeNumber = typeof payload.size === "number" ? payload.size : typeof payload.size === "string" ? Number(payload.size) : null;
1162
- return {
1163
- events: [{
1164
- provider: "gcp-gcs",
1165
- eventType,
1166
- eventTime: payload.updated ?? payload.timeCreated ?? receivedAt,
1167
- bucket: payload.bucket,
1168
- key: payload.name,
1169
- size: sizeNumber !== null && Number.isFinite(sizeNumber) ? sizeNumber : null,
1170
- etag: payload.etag ?? null,
1171
- region: null,
1172
- sourceIp: null,
1173
- messageId: push.message.messageId ?? null,
1174
- receivedAt,
1175
- raw: payload
1176
- }],
1177
- provider: "gcp-gcs",
1178
- receivedAt,
1179
- messageId: push.message.messageId ?? null,
1180
- topic: push.subscription ?? null,
1181
- raw: push
1182
- };
1183
- }
1184
- /**
1185
- * `triggers.autoIngestGcsPubSub` — receives GCP Pub/Sub push deliveries,
1186
- * verifies the Google-signed OIDC JWT against Google's JWKS, and
1187
- * emits normalized GCS object events extracted from the Pub/Sub
1188
- * message body.
1189
- */
1190
- function autoIngestGcsPubSub(input) {
1191
- const { verifierOptions, path, ...rest } = input ?? {};
1192
- return createWebhookTriggerBindingFactory({
1193
- defaultName: "Snowflake Auto-Ingest — GCS Pub/Sub",
1194
- defaultDescription: "Receives Snowpipe auto-ingest notifications via GCP Pub/Sub push. Verifies the Google-signed OIDC JWT in the Authorization header and normalizes GCS object events.",
1195
- path: resolveWebhookPath(path, "/snowflake/auto-ingest/gcs-pubsub"),
1196
- method: "POST",
1197
- payload: gcsWebhookPayloadSchema,
1198
- credentialSets: [snowflake, snowflakeGcsPubSubWebhookCredentialSet],
1199
- response: { successStatus: 204 },
1200
- verify: async (request, ctx) => {
1201
- const creds = ctx.credentials["snowflake-gcs-pubsub-webhook"];
1202
- try {
1203
- parsePubSubPush(request.rawBody);
1204
- await verifyPubSubPushAuthorization(request.headers.authorization, {
1205
- expectedAudience: creds.expectedAudience,
1206
- expectedServiceAccount: creds.expectedServiceAccount,
1207
- ...creds.issuer ? { issuer: creds.issuer } : {},
1208
- ...creds.jwksUri ? { jwksUri: creds.jwksUri } : {},
1209
- ...verifierOptions ?? {}
1210
- });
1211
- } catch (err) {
1212
- if (err instanceof SnowflakeWebhookVerificationError) throw err;
1213
- throw new SnowflakeWebhookVerificationError({
1214
- message: "Unexpected error verifying Pub/Sub push.",
1215
- reason: "unknown",
1216
- provider: "gcp-gcs",
1217
- cause: err
1218
- });
1219
- }
1220
- },
1221
- mapPayload: (payload) => {
1222
- const batch = gcsPushToBatch(payload);
1223
- return batch.events.length > 0 ? batch : null;
1224
- },
1225
- idempotencyKey: (_payload, mapped) => mapped ? `pubsub:${mapped.messageId ?? "no-id"}` : void 0
1226
- })(rest);
1227
- }
1228
- /**
1229
- * Event Grid wire format is `AzureEventGridEvent[]`. Keystroke's
1230
- * webhook primitive validates a single payload schema — so we accept
1231
- * either a single event or a wrapper object. The runtime reparses the
1232
- * raw body to preserve the array shape.
1233
- */
1234
- const azureWebhookPayloadSchema = z.union([z.array(azureEventGridEventSchema), azureEventGridEventSchema]);
1235
- function azureBatchToBatch(events, rawBody) {
1236
- const receivedAt = isoNowUtc();
1237
- const mapped = [];
1238
- let topic = null;
1239
- for (const event of events) {
1240
- if (event.eventType === EVENT_GRID_VALIDATION_EVENT_TYPE) continue;
1241
- topic = event.topic ?? topic;
1242
- const loc = parseAzureBlobLocation(event);
1243
- const data = event.data;
1244
- mapped.push({
1245
- provider: "azure-blob",
1246
- eventType: event.eventType,
1247
- eventTime: event.eventTime,
1248
- bucket: loc.bucket,
1249
- key: loc.key,
1250
- size: typeof data.contentLength === "number" ? data.contentLength : null,
1251
- etag: typeof data.eTag === "string" ? data.eTag : null,
1252
- region: null,
1253
- sourceIp: null,
1254
- messageId: event.id,
1255
- receivedAt,
1256
- raw: event
1257
- });
1258
- }
1259
- return {
1260
- events: mapped,
1261
- provider: "azure-blob",
1262
- receivedAt,
1263
- messageId: events[0]?.id ?? null,
1264
- topic,
1265
- raw: JSON.parse(rawBody)
1266
- };
1267
- }
1268
- /**
1269
- * `triggers.autoIngestAzureEventGrid` — receives Azure Event Grid
1270
- * deliveries. Validates shared-secret header (if configured), parses
1271
- * the batch, and emits normalized Azure Blob object events. The
1272
- * synchronous subscription-validation handshake is NOT supported
1273
- * through the Keystroke static-response primitive; operators must
1274
- * use manual validation URLs (see IMPLEMENTATION_NOTES.md).
1275
- */
1276
- function autoIngestAzureEventGrid(input) {
1277
- const { verifierOptions, path, ...rest } = input ?? {};
1278
- return createWebhookTriggerBindingFactory({
1279
- defaultName: "Snowflake Auto-Ingest — Azure Event Grid",
1280
- defaultDescription: "Receives Snowpipe auto-ingest notifications via Azure Event Grid. Validates shared-secret header, drops subscription-validation events (manual handshake required), and normalizes Azure Blob object events.",
1281
- path: resolveWebhookPath(path, "/snowflake/auto-ingest/azure-eventgrid"),
1282
- method: "POST",
1283
- payload: azureWebhookPayloadSchema,
1284
- credentialSets: [snowflake, snowflakeAzureEventGridWebhookCredentialSet],
1285
- response: { successStatus: 200 },
1286
- verify: async (request, ctx) => {
1287
- const creds = ctx.credentials["snowflake-azure-eventgrid-webhook"];
1288
- try {
1289
- verifyEventGridRequest({
1290
- rawBody: request.rawBody,
1291
- headers: request.headers
1292
- }, {
1293
- ...creds.sharedSecret ? { sharedSecret: creds.sharedSecret } : {},
1294
- ...creds.sharedSecretHeader ? { sharedSecretHeader: creds.sharedSecretHeader } : {},
1295
- ...creds.expectedTopicArn ? { expectedTopic: creds.expectedTopicArn } : {},
1296
- ...verifierOptions ?? {}
1297
- });
1298
- } catch (err) {
1299
- if (err instanceof SnowflakeWebhookVerificationError) throw err;
1300
- throw new SnowflakeWebhookVerificationError({
1301
- message: "Unexpected error verifying Event Grid request.",
1302
- reason: "unknown",
1303
- provider: "azure-blob",
1304
- cause: err
1305
- });
1306
- }
1307
- },
1308
- mapPayload: (payload) => {
1309
- const events = Array.isArray(payload) ? payload : [payload];
1310
- const batch = azureBatchToBatch(events, JSON.stringify(events));
1311
- return batch.events.length > 0 ? batch : null;
1312
- },
1313
- idempotencyKey: (_payload, mapped) => mapped ? `eventgrid:${mapped.messageId ?? "no-id"}` : void 0
1314
- })(rest);
1315
- }
1316
- /**
1317
- * Convenience namespace grouping every polling trigger in this package.
1318
- * Equivalent to calling the individual factory functions directly.
1319
- */
1320
1026
  const polling = Object.freeze({
1321
1027
  streamHasNewRows,
1322
1028
  streamAppendOnly,
@@ -1341,23 +1047,6 @@ const polling = Object.freeze({
1341
1047
  statusPageIncident,
1342
1048
  statusPageComponentChanged
1343
1049
  });
1344
- /**
1345
- * Convenience namespace grouping every auto-ingest webhook trigger.
1346
- */
1347
- const webhooks = Object.freeze({
1348
- autoIngestS3Sns,
1349
- autoIngestGcsPubSub,
1350
- autoIngestAzureEventGrid
1351
- });
1352
- /**
1353
- * Nested `autoIngest` namespace per the spec (§7 PLAN.md) so callers
1354
- * can reach the three webhook triggers via `triggers.autoIngest.<cloud>`.
1355
- */
1356
- const autoIngest = Object.freeze({
1357
- s3Sns: autoIngestS3Sns,
1358
- gcsPubSub: autoIngestGcsPubSub,
1359
- azureEventGrid: autoIngestAzureEventGrid
1360
- });
1361
1050
 
1362
1051
  //#endregion
1363
- export { autoIngest, autoIngestAzureEventGrid, autoIngestGcsPubSub, autoIngestS3Sns, databaseCreated, databaseDropped, grantsChanged, pipeLoadCompleted, pipeLoadFailed, polling, roleCreated, roleDropped, scheduledDelta, scheduledSql, shareChanged, statementCompleted, statusPageComponentChanged, statusPageIncident, streamAppendOnly, streamHasNewRows, tableCreated, tableDropped, taskRunCompleted, userCreated, userDropped, warehouseCreditsUsage, warehouseQueueDepth, webhooks };
1052
+ export { databaseCreated, databaseDropped, grantsChanged, pipeLoadCompleted, pipeLoadFailed, polling, roleCreated, roleDropped, scheduledDelta, scheduledSql, shareChanged, statementCompleted, statusPageComponentChanged, statusPageIncident, streamAppendOnly, streamHasNewRows, tableCreated, tableDropped, taskRunCompleted, userCreated, userDropped, warehouseCreditsUsage, warehouseQueueDepth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keystrokehq/snowflake",
3
- "version": "0.0.1",
3
+ "version": "0.0.5",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -147,27 +147,21 @@
147
147
  "README.md",
148
148
  "LICENSE"
149
149
  ],
150
- "scripts": {
151
- "typecheck": "tsgo --build",
152
- "build": "tsdown",
153
- "lint": "biome check .",
154
- "test:unit": "vitest run --passWithNoTests --project unit",
155
- "test:int": "vitest run --passWithNoTests --project int",
156
- "prepublishOnly": "pnpm build && pnpm test:unit",
157
- "lint:fix": "biome check --write ."
158
- },
159
150
  "dependencies": {
160
- "@keystrokehq/integration-authoring": "^0.0.1",
161
- "@keystrokehq/core": "^0.0.5",
151
+ "@keystrokehq/integration-authoring": "^0.0.5",
162
152
  "zod": "^4.3.6"
163
153
  },
154
+ "peerDependencies": {
155
+ "@keystrokehq/core": ">=0.0.7 <0.1.0"
156
+ },
164
157
  "devDependencies": {
165
- "@types/node": "catalog:",
166
- "@keystrokehq/test-utils": "workspace:*",
167
- "@keystrokehq/typescript-config": "workspace:*",
168
- "tsdown": "catalog:",
169
- "typescript": "catalog:",
170
- "vitest": "catalog:"
158
+ "@types/node": "^22.19.11",
159
+ "tsdown": "^0.20.3",
160
+ "typescript": "^5.9.3",
161
+ "vitest": "^4.0.18",
162
+ "@keystrokehq/core": "^0.0.7",
163
+ "@keystrokehq/test-utils": "0.0.0",
164
+ "@keystrokehq/typescript-config": "0.0.0"
171
165
  },
172
166
  "keywords": [
173
167
  "snowflake",
@@ -186,5 +180,13 @@
186
180
  "publishConfig": {
187
181
  "access": "public",
188
182
  "registry": "https://registry.npmjs.org/"
183
+ },
184
+ "scripts": {
185
+ "typecheck": "tsgo --build",
186
+ "build": "tsdown",
187
+ "lint": "biome check .",
188
+ "test:unit": "vitest run --passWithNoTests --project unit",
189
+ "test:int": "vitest run --passWithNoTests --project int",
190
+ "lint:fix": "biome check --write ."
189
191
  }
190
- }
192
+ }