svix 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/index.d.mts +1039 -604
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +4410 -3642
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/src/api/destination.ts +133 -0
  7. package/src/api/destinationTransformation.ts +55 -0
  8. package/src/api_internal/destination.ts +12 -0
  9. package/src/api_internal/destinationAutoconfig.ts +27 -0
  10. package/src/api_internal/endpoint.ts +8 -3
  11. package/src/api_internal/{endpointAutoConfig.ts → endpointAutoConfigDeprecated.ts} +1 -1
  12. package/src/api_internal/endpointAutoconfig.ts +50 -0
  13. package/src/autoconfig.test.ts +169 -4
  14. package/src/autoconfig.ts +89 -26
  15. package/src/autoconfigConsumer.ts +90 -16
  16. package/src/index.ts +5 -0
  17. package/src/models/autoConfigSubscriptionOut.ts +38 -0
  18. package/src/models/destinationIn.ts +303 -0
  19. package/src/models/destinationOut.ts +295 -0
  20. package/src/models/destinationPatch.ts +302 -0
  21. package/src/models/destinationStatus.ts +18 -0
  22. package/src/models/destinationStatusIn.ts +16 -0
  23. package/src/models/destinationTransformIn.ts +19 -0
  24. package/src/models/destinationTransformationOut.ts +22 -0
  25. package/src/models/fifoEndpointConfigIn.ts +25 -0
  26. package/src/models/index.ts +14 -0
  27. package/src/models/ingestSourceIn.ts +12 -0
  28. package/src/models/ingestSourceOut.ts +12 -0
  29. package/src/models/listResponseDestinationOut.ts +31 -0
  30. package/src/models/mergeConfig.ts +19 -0
  31. package/src/models/mergeConfigOut.ts +15 -0
  32. package/src/models/postgresConfigIn.ts +44 -0
  33. package/src/models/postgresConfigOut.ts +25 -0
  34. package/src/models/postgresConfigPatch.ts +28 -0
  35. package/src/models/s3ConfigIn.ts +15 -3
  36. package/src/models/s3ConfigOut.ts +7 -1
  37. package/src/models/s3ConfigPatch.ts +6 -0
  38. package/src/models/status.ts +16 -0
  39. package/src/models/streamSinkIn.ts +12 -0
  40. package/src/models/streamSinkOut.ts +27 -0
  41. package/src/models/streamSinkPatch.ts +15 -0
  42. package/src/request.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svix",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Svix webhooks API client and webhook verification library",
5
5
  "author": "svix",
6
6
  "repository": "https://github.com/svix/svix-webhooks",
@@ -0,0 +1,133 @@
1
+ // this file is @generated
2
+
3
+ import { type DestinationIn, DestinationInSerializer } from "../models/destinationIn";
4
+ import { type DestinationOut, DestinationOutSerializer } from "../models/destinationOut";
5
+ import {
6
+ type DestinationPatch,
7
+ DestinationPatchSerializer,
8
+ } from "../models/destinationPatch";
9
+ import {
10
+ type ListResponseDestinationOut,
11
+ ListResponseDestinationOutSerializer,
12
+ } from "../models/listResponseDestinationOut";
13
+ import type { Ordering } from "../models/ordering";
14
+ import { DestinationTransformation } from "./destinationTransformation";
15
+ import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
16
+
17
+ export interface DestinationListOptions {
18
+ /** Limit the number of returned items */
19
+ limit?: number;
20
+ /** The iterator returned from a prior invocation */
21
+ iterator?: string | null;
22
+ /** The sorting order of the returned items */
23
+ order?: Ordering;
24
+ }
25
+
26
+ export interface DestinationCreateOptions {
27
+ idempotencyKey?: string;
28
+ }
29
+
30
+ export class Destination {
31
+ public constructor(private readonly requestCtx: SvixRequestContext) {}
32
+
33
+ public get transformation() {
34
+ return new DestinationTransformation(this.requestCtx);
35
+ }
36
+
37
+ /** List of all the application's destinations. */
38
+ public async list(
39
+ appId: string,
40
+ options?: DestinationListOptions
41
+ ): Promise<ListResponseDestinationOut> {
42
+ const request = new SvixRequest(HttpMethod.GET, "/api/v1/app/{app_id}/destination");
43
+
44
+ request.setPathParam("app_id", appId);
45
+ request.setQueryParams({
46
+ limit: options?.limit,
47
+ iterator: options?.iterator,
48
+ order: options?.order,
49
+ });
50
+
51
+ return await request.send(
52
+ this.requestCtx,
53
+ ListResponseDestinationOutSerializer._fromJsonObject
54
+ );
55
+ }
56
+
57
+ /** Creates a new destination. */
58
+ public async create(
59
+ appId: string,
60
+ destinationIn: DestinationIn,
61
+ options?: DestinationCreateOptions
62
+ ): Promise<DestinationOut> {
63
+ const request = new SvixRequest(HttpMethod.POST, "/api/v1/app/{app_id}/destination");
64
+
65
+ request.setPathParam("app_id", appId);
66
+ request.setHeaderParam("idempotency-key", options?.idempotencyKey);
67
+ request.setBody(DestinationInSerializer._toJsonObject(destinationIn));
68
+
69
+ return await request.send(this.requestCtx, DestinationOutSerializer._fromJsonObject);
70
+ }
71
+
72
+ /** Get a destination by id or uid. */
73
+ public async get(appId: string, destinationId: string): Promise<DestinationOut> {
74
+ const request = new SvixRequest(
75
+ HttpMethod.GET,
76
+ "/api/v1/app/{app_id}/destination/{destination_id}"
77
+ );
78
+
79
+ request.setPathParam("app_id", appId);
80
+ request.setPathParam("destination_id", destinationId);
81
+
82
+ return await request.send(this.requestCtx, DestinationOutSerializer._fromJsonObject);
83
+ }
84
+
85
+ /** Create or update a destination. */
86
+ public async upsert(
87
+ appId: string,
88
+ destinationId: string,
89
+ destinationIn: DestinationIn
90
+ ): Promise<DestinationOut> {
91
+ const request = new SvixRequest(
92
+ HttpMethod.PUT,
93
+ "/api/v1/app/{app_id}/destination/{destination_id}"
94
+ );
95
+
96
+ request.setPathParam("app_id", appId);
97
+ request.setPathParam("destination_id", destinationId);
98
+ request.setBody(DestinationInSerializer._toJsonObject(destinationIn));
99
+
100
+ return await request.send(this.requestCtx, DestinationOutSerializer._fromJsonObject);
101
+ }
102
+
103
+ /** Delete a destination. */
104
+ public async delete(appId: string, destinationId: string): Promise<void> {
105
+ const request = new SvixRequest(
106
+ HttpMethod.DELETE,
107
+ "/api/v1/app/{app_id}/destination/{destination_id}"
108
+ );
109
+
110
+ request.setPathParam("app_id", appId);
111
+ request.setPathParam("destination_id", destinationId);
112
+
113
+ return await request.sendNoResponseBody(this.requestCtx);
114
+ }
115
+
116
+ /** Partially update a destination. */
117
+ public async patch(
118
+ appId: string,
119
+ destinationId: string,
120
+ destinationPatch: DestinationPatch
121
+ ): Promise<DestinationOut> {
122
+ const request = new SvixRequest(
123
+ HttpMethod.PATCH,
124
+ "/api/v1/app/{app_id}/destination/{destination_id}"
125
+ );
126
+
127
+ request.setPathParam("app_id", appId);
128
+ request.setPathParam("destination_id", destinationId);
129
+ request.setBody(DestinationPatchSerializer._toJsonObject(destinationPatch));
130
+
131
+ return await request.send(this.requestCtx, DestinationOutSerializer._fromJsonObject);
132
+ }
133
+ }
@@ -0,0 +1,55 @@
1
+ // this file is @generated
2
+
3
+ import {
4
+ type DestinationTransformIn,
5
+ DestinationTransformInSerializer,
6
+ } from "../models/destinationTransformIn";
7
+ import {
8
+ type DestinationTransformationOut,
9
+ DestinationTransformationOutSerializer,
10
+ } from "../models/destinationTransformationOut";
11
+ import { type EmptyResponse, EmptyResponseSerializer } from "../models/emptyResponse";
12
+ import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
13
+
14
+ export class DestinationTransformation {
15
+ public constructor(private readonly requestCtx: SvixRequestContext) {}
16
+
17
+ /** Get the transformation code associated with this destination. */
18
+ public async get(
19
+ appId: string,
20
+ destinationId: string
21
+ ): Promise<DestinationTransformationOut> {
22
+ const request = new SvixRequest(
23
+ HttpMethod.GET,
24
+ "/api/v1/app/{app_id}/destination/{destination_id}/transformation"
25
+ );
26
+
27
+ request.setPathParam("app_id", appId);
28
+ request.setPathParam("destination_id", destinationId);
29
+
30
+ return await request.send(
31
+ this.requestCtx,
32
+ DestinationTransformationOutSerializer._fromJsonObject
33
+ );
34
+ }
35
+
36
+ /** Set or unset the transformation code associated with this destination. */
37
+ public async patch(
38
+ appId: string,
39
+ destinationId: string,
40
+ destinationTransformIn: DestinationTransformIn = {}
41
+ ): Promise<EmptyResponse> {
42
+ const request = new SvixRequest(
43
+ HttpMethod.PATCH,
44
+ "/api/v1/app/{app_id}/destination/{destination_id}/transformation"
45
+ );
46
+
47
+ request.setPathParam("app_id", appId);
48
+ request.setPathParam("destination_id", destinationId);
49
+ request.setBody(
50
+ DestinationTransformInSerializer._toJsonObject(destinationTransformIn)
51
+ );
52
+
53
+ return await request.send(this.requestCtx, EmptyResponseSerializer._fromJsonObject);
54
+ }
55
+ }
@@ -0,0 +1,12 @@
1
+ // this file is @generated
2
+
3
+ import { DestinationAutoconfig } from "./destinationAutoconfig";
4
+ import type { SvixRequestContext } from "../request";
5
+
6
+ export class Destination {
7
+ public constructor(private readonly requestCtx: SvixRequestContext) {}
8
+
9
+ public get autoconfig() {
10
+ return new DestinationAutoconfig(this.requestCtx);
11
+ }
12
+ }
@@ -0,0 +1,27 @@
1
+ // this file is @generated
2
+
3
+ import { type DestinationIn, DestinationInSerializer } from "../models/destinationIn";
4
+ import { type DestinationOut, DestinationOutSerializer } from "../models/destinationOut";
5
+ import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
6
+
7
+ export class DestinationAutoconfig {
8
+ public constructor(private readonly requestCtx: SvixRequestContext) {}
9
+
10
+ /** Create or update the destination for an AutoConfig subscription. */
11
+ public async subscribe(
12
+ appId: string,
13
+ autoconfigId: string,
14
+ destinationIn: DestinationIn
15
+ ): Promise<DestinationOut> {
16
+ const request = new SvixRequest(
17
+ HttpMethod.PUT,
18
+ "/api/v1/app/{app_id}/autoconfig/{autoconfig_id}/destination"
19
+ );
20
+
21
+ request.setPathParam("app_id", appId);
22
+ request.setPathParam("autoconfig_id", autoconfigId);
23
+ request.setBody(DestinationInSerializer._toJsonObject(destinationIn));
24
+
25
+ return await request.send(this.requestCtx, DestinationOutSerializer._fromJsonObject);
26
+ }
27
+ }
@@ -4,14 +4,19 @@ import {
4
4
  type EndpointTransformationIn,
5
5
  EndpointTransformationInSerializer,
6
6
  } from "../models/endpointTransformationIn";
7
- import { EndpointAutoConfig } from "./endpointAutoConfig";
7
+ import { EndpointAutoConfigDeprecated } from "./endpointAutoConfigDeprecated";
8
+ import { EndpointAutoconfig } from "./endpointAutoconfig";
8
9
  import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
9
10
 
10
11
  export class Endpoint {
11
12
  public constructor(private readonly requestCtx: SvixRequestContext) {}
12
13
 
13
- public get autoConfig() {
14
- return new EndpointAutoConfig(this.requestCtx);
14
+ public get autoConfigDeprecated() {
15
+ return new EndpointAutoConfigDeprecated(this.requestCtx);
16
+ }
17
+
18
+ public get autoconfig() {
19
+ return new EndpointAutoconfig(this.requestCtx);
15
20
  }
16
21
 
17
22
  /**
@@ -4,7 +4,7 @@ import { type EndpointOut, EndpointOutSerializer } from "../models/endpointOut";
4
4
  import { type SubscribeIn, SubscribeInSerializer } from "../models/subscribeIn";
5
5
  import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
6
6
 
7
- export class EndpointAutoConfig {
7
+ export class EndpointAutoConfigDeprecated {
8
8
  public constructor(private readonly requestCtx: SvixRequestContext) {}
9
9
 
10
10
  /** Update an auto-config endpoint by providing endpoint details. */
@@ -0,0 +1,50 @@
1
+ // this file is @generated
2
+
3
+ import {
4
+ type AutoConfigSubscriptionOut,
5
+ AutoConfigSubscriptionOutSerializer,
6
+ } from "../models/autoConfigSubscriptionOut";
7
+ import { type EndpointIn, EndpointInSerializer } from "../models/endpointIn";
8
+ import { type EndpointOut, EndpointOutSerializer } from "../models/endpointOut";
9
+ import { HttpMethod, SvixRequest, type SvixRequestContext } from "../request";
10
+
11
+ export class EndpointAutoconfig {
12
+ public constructor(private readonly requestCtx: SvixRequestContext) {}
13
+
14
+ /** Get an AutoConfig subscription, including the bound endpoint or destination if any. */
15
+ public async get(
16
+ appId: string,
17
+ autoconfigId: string
18
+ ): Promise<AutoConfigSubscriptionOut> {
19
+ const request = new SvixRequest(
20
+ HttpMethod.GET,
21
+ "/api/v1/app/{app_id}/autoconfig/{autoconfig_id}"
22
+ );
23
+
24
+ request.setPathParam("app_id", appId);
25
+ request.setPathParam("autoconfig_id", autoconfigId);
26
+
27
+ return await request.send(
28
+ this.requestCtx,
29
+ AutoConfigSubscriptionOutSerializer._fromJsonObject
30
+ );
31
+ }
32
+
33
+ /** Create or update the HTTP endpoint for an AutoConfig subscription. */
34
+ public async subscribe(
35
+ appId: string,
36
+ autoconfigId: string,
37
+ endpointIn: EndpointIn
38
+ ): Promise<EndpointOut> {
39
+ const request = new SvixRequest(
40
+ HttpMethod.PUT,
41
+ "/api/v1/app/{app_id}/autoconfig/{autoconfig_id}/endpoint"
42
+ );
43
+
44
+ request.setPathParam("app_id", appId);
45
+ request.setPathParam("autoconfig_id", autoconfigId);
46
+ request.setBody(EndpointInSerializer._toJsonObject(endpointIn));
47
+
48
+ return await request.send(this.requestCtx, EndpointOutSerializer._fromJsonObject);
49
+ }
50
+ }
@@ -1,13 +1,29 @@
1
1
  import { test } from "node:test";
2
2
  import { strict as assert } from "node:assert/strict";
3
+ import * as mockttp from "mockttp";
3
4
 
4
- import { AutoConfig, AutoConfigError, Webhook } from "./index";
5
+ import { AutoConfig, AutoConfigConsumer, AutoConfigError, Webhook } from "./index";
5
6
 
6
- function makeTokenV1(payload: Record<string, string>): string {
7
+ function makeToken(
8
+ prefix: "auto_v1_" | "auto_v2_",
9
+ payload: Record<string, string>
10
+ ): string {
7
11
  const json = JSON.stringify(payload);
8
- return `auto_v1_${Buffer.from(json, "utf8").toString("base64")}`;
12
+ return `${prefix}${Buffer.from(json, "utf8").toString("base64")}`;
13
+ }
14
+
15
+ function makeTokenV1(payload: Record<string, string>): string {
16
+ return makeToken("auto_v1_", payload);
17
+ }
18
+
19
+ function makeTokenV2(payload: Record<string, string>): string {
20
+ return makeToken("auto_v2_", payload);
9
21
  }
10
22
 
23
+ const endpointOut = `{"id":"ep_2","metadata":{},"url":"https://consumer.example/webhook","description":"","createdAt":"2019-08-24T14:15:22Z","updatedAt":"2019-08-24T14:15:22Z"}`;
24
+ const destinationOut = `{"type":"pollingEndpoint","config":{},"id":"dst_123","status":"enabled","currentIterator":"0","createdAt":"2019-08-24T14:15:22Z","updatedAt":"2019-08-24T14:15:22Z","batchSize":100,"maxWaitSecs":10,"metadata":{}}`;
25
+ const pollOut = `{"data":[],"done":true}`;
26
+
11
27
  test("AutoConfig accepts valid auto_v1 token and verify matches Webhook", () => {
12
28
  const esec = "whsec_Zm9v";
13
29
  const token = makeTokenV1({
@@ -42,10 +58,159 @@ test("AutoConfig rejects wrong prefix", () => {
42
58
  tok: "t",
43
59
  });
44
60
  const token = `wrong_${Buffer.from(json, "utf8").toString("base64")}`;
45
- assert.throws(() => new AutoConfig(token, { url: "https://x" }), AutoConfigError);
61
+ assert.throws(
62
+ () => new AutoConfig(token, { url: "https://x" }),
63
+ (err: unknown) =>
64
+ err instanceof AutoConfigError &&
65
+ err.message ===
66
+ "Unsupported token version. You might need to update the Svix SDK to use this token"
67
+ );
46
68
  });
47
69
 
48
70
  test("AutoConfig rejects invalid JSON payload", () => {
49
71
  const token = `auto_v1_${Buffer.from("not json", "utf8").toString("base64")}`;
50
72
  assert.throws(() => new AutoConfig(token, { url: "https://x" }), AutoConfigError);
51
73
  });
74
+
75
+ test("AutoConfig accepts valid auto_v2 token and verify matches Webhook", () => {
76
+ const esec = "whsec_Zm9v";
77
+ const token = makeTokenV2({
78
+ aid: "app_1",
79
+ sid: "acfg_2",
80
+ surl: "https://api.example.test",
81
+ esec,
82
+ tok: "sk_test_xyz",
83
+ });
84
+ const ac = new AutoConfig(token, { url: "https://consumer.example/webhook" });
85
+
86
+ const payload = '{"hello":"world"}';
87
+ const id = "msg_test_autoconfig_v2";
88
+ const ts = new Date();
89
+ const wh = new Webhook(esec);
90
+ const sig = wh.sign(id, ts, payload);
91
+ const headers = {
92
+ "svix-id": id,
93
+ "svix-timestamp": Math.floor(ts.getTime() / 1000).toString(),
94
+ "svix-signature": sig,
95
+ };
96
+
97
+ ac.verify(payload, headers);
98
+ });
99
+
100
+ test("subscribe", async (t) => {
101
+ const mockServer = mockttp.getLocal();
102
+ t.beforeEach(async () => await mockServer.start(0));
103
+ t.afterEach(async () => await mockServer.stop());
104
+
105
+ await t.test("v1 AutoConfig", async () => {
106
+ const mock = await mockServer
107
+ .forPut("/api/v1/app/app_1/endpoint/ep_2/auto-config")
108
+ .thenReply(200, endpointOut);
109
+ const token = makeTokenV1({
110
+ aid: "app_1",
111
+ eid: "ep_2",
112
+ surl: mockServer.url,
113
+ esec: "whsec_Zm9v",
114
+ tok: "sk_test_xyz",
115
+ });
116
+ const ac = new AutoConfig(token, { url: "https://consumer.example/webhook" });
117
+ const out = await ac.subscribe();
118
+ assert.equal(out.id, "ep_2");
119
+ const requests = await mock.getSeenRequests();
120
+ assert.equal(requests.length, 1);
121
+ assert.equal(
122
+ await requests[0].body.getText(),
123
+ `{"endpoint":{"url":"https://consumer.example/webhook"}}`
124
+ );
125
+ });
126
+
127
+ await t.test("v2 AutoConfig", async () => {
128
+ const mock = await mockServer
129
+ .forPut("/api/v1/app/app_1/autoconfig/acfg_2/endpoint")
130
+ .thenReply(200, endpointOut);
131
+ const token = makeTokenV2({
132
+ aid: "app_1",
133
+ sid: "acfg_2",
134
+ surl: mockServer.url,
135
+ esec: "whsec_Zm9v",
136
+ tok: "sk_test_xyz",
137
+ });
138
+ const ac = new AutoConfig(token, { url: "https://consumer.example/webhook" });
139
+ const out = await ac.subscribe();
140
+ assert.equal(out.id, "ep_2");
141
+ const requests = await mock.getSeenRequests();
142
+ assert.equal(requests.length, 1);
143
+ assert.equal(
144
+ await requests[0].body.getText(),
145
+ `{"url":"https://consumer.example/webhook"}`
146
+ );
147
+ });
148
+
149
+ await t.test("v1 AutoConfigConsumer", async () => {
150
+ const mock = await mockServer
151
+ .forPut("/api/v1/app/app_1/endpoint/ep_2/auto-config")
152
+ .thenReply(200, endpointOut);
153
+ const token = makeTokenV1({
154
+ aid: "app_1",
155
+ eid: "ep_2",
156
+ surl: mockServer.url,
157
+ esec: "whsec_Zm9v",
158
+ tok: "sk_test_xyz",
159
+ });
160
+ const consumer = new AutoConfigConsumer(token, {
161
+ eventTypes: ["issue.opened"],
162
+ });
163
+ await consumer.subscribe();
164
+ const requests = await mock.getSeenRequests();
165
+ assert.equal(requests.length, 1);
166
+ assert.equal(
167
+ await requests[0].body.getText(),
168
+ `{"sink":{"type":"poller","config":{"eventTypes":["issue.opened"]}}}`
169
+ );
170
+ });
171
+
172
+ await t.test("v2 AutoConfigConsumer", async () => {
173
+ const mock = await mockServer
174
+ .forPut("/api/v1/app/app_1/autoconfig/acfg_2/destination")
175
+ .thenReply(200, destinationOut);
176
+ const token = makeTokenV2({
177
+ aid: "app_1",
178
+ sid: "acfg_2",
179
+ surl: mockServer.url,
180
+ esec: "whsec_Zm9v",
181
+ tok: "sk_test_xyz",
182
+ });
183
+ const consumer = new AutoConfigConsumer(token, {
184
+ channels: ["ch1"],
185
+ });
186
+ const out = await consumer.subscribe();
187
+ assert.equal(out.id, "dst_123");
188
+ const requests = await mock.getSeenRequests();
189
+ assert.equal(requests.length, 1);
190
+ assert.equal(
191
+ await requests[0].body.getText(),
192
+ `{"type":"pollingEndpoint","config":{},"channels":["ch1"]}`
193
+ );
194
+ });
195
+
196
+ await t.test("v2 receive gets subscription", async () => {
197
+ const subscriptionOut = `{"id":"auto_1srOrx2ZWZBpBUvZwXKQmoEYga2","tokenCensored":"***","createdAt":"2019-08-24T14:15:22Z","status":"active","destId":"dst_123"}`;
198
+ await mockServer
199
+ .forGet("/api/v1/app/app_1/autoconfig/acfg_2")
200
+ .thenReply(200, subscriptionOut);
201
+ const pollMock = await mockServer
202
+ .forGet("/api/v1/app/app_1/polling-endpoint/dst_123/consumer/c1")
203
+ .thenReply(200, pollOut);
204
+ const token = makeTokenV2({
205
+ aid: "app_1",
206
+ sid: "acfg_2",
207
+ surl: mockServer.url,
208
+ esec: "whsec_Zm9v",
209
+ tok: "sk_test_xyz",
210
+ });
211
+ const consumer = new AutoConfigConsumer(token, {});
212
+ await consumer.receive("c1");
213
+ const requests = await pollMock.getSeenRequests();
214
+ assert.equal(requests.length, 1);
215
+ });
216
+ });
package/src/autoconfig.ts CHANGED
@@ -10,12 +10,34 @@ import {
10
10
  } from "./webhook";
11
11
 
12
12
  const AUTOCONFIG_TOKEN_PREFIX_V1 = "auto_v1_";
13
+ const AUTOCONFIG_TOKEN_PREFIX_V2 = "auto_v2_";
14
+
15
+ const UNSUPPORTED_TOKEN_VERSION =
16
+ "Unsupported token version. You might need to update the Svix SDK to use this token";
13
17
 
14
18
  export interface AutoConfigTokenContentV1 {
19
+ // Application ID
15
20
  aid: string;
21
+ // Endpoint ID
16
22
  eid: string;
23
+ // Server URL
24
+ surl: string;
25
+ // Endpoint secret
26
+ esec: string;
27
+ // Token
28
+ tok: string;
29
+ }
30
+
31
+ export interface AutoConfigTokenContentV2 {
32
+ // Application ID
33
+ aid: string;
34
+ // Autoconfig ID
35
+ sid: string;
36
+ // Server URL
17
37
  surl: string;
38
+ // Endpoint secret
18
39
  esec: string;
40
+ // Token
19
41
  tok: string;
20
42
  }
21
43
 
@@ -43,66 +65,107 @@ export function isAutoConfigTokenContentV1(
43
65
  );
44
66
  }
45
67
 
46
- export function decodeAutoconfigTokenV1(token: string): AutoConfigTokenContentV1 {
47
- if (!token.startsWith(AUTOCONFIG_TOKEN_PREFIX_V1)) {
48
- throw new AutoConfigError();
68
+ function isAutoConfigTokenContentV2(value: unknown): value is AutoConfigTokenContentV2 {
69
+ if (typeof value !== "object" || value === null) {
70
+ return false;
49
71
  }
50
- const b64 = token.slice(AUTOCONFIG_TOKEN_PREFIX_V1.length);
51
- let json: string;
72
+ const { aid, sid, surl, esec, tok } = value as AutoConfigTokenContentV2;
73
+ return (
74
+ typeof aid === "string" &&
75
+ typeof sid === "string" &&
76
+ typeof surl === "string" &&
77
+ typeof esec === "string" &&
78
+ typeof tok === "string"
79
+ );
80
+ }
52
81
 
53
- try {
54
- json = Buffer.from(b64, "base64").toString("utf8");
55
- } catch {
56
- throw new AutoConfigError();
82
+ function parseTokenPayload(token: string, prefix: string): unknown {
83
+ if (!token.startsWith(prefix)) {
84
+ throw new AutoConfigError(UNSUPPORTED_TOKEN_VERSION);
57
85
  }
58
-
59
- let parsed: unknown;
60
86
  try {
61
- parsed = JSON.parse(json);
87
+ return JSON.parse(Buffer.from(token.slice(prefix.length), "base64").toString("utf8"));
62
88
  } catch {
63
89
  throw new AutoConfigError();
64
90
  }
91
+ }
65
92
 
93
+ export function decodeAutoconfigTokenV1(token: string): AutoConfigTokenContentV1 {
94
+ const parsed = parseTokenPayload(token, AUTOCONFIG_TOKEN_PREFIX_V1);
66
95
  if (!isAutoConfigTokenContentV1(parsed)) {
67
96
  throw new AutoConfigError();
68
97
  }
98
+ return parsed;
99
+ }
69
100
 
101
+ export function decodeAutoconfigTokenV2(token: string): AutoConfigTokenContentV2 {
102
+ const parsed = parseTokenPayload(token, AUTOCONFIG_TOKEN_PREFIX_V2);
103
+ if (!isAutoConfigTokenContentV2(parsed)) {
104
+ throw new AutoConfigError();
105
+ }
70
106
  return parsed;
71
107
  }
72
108
 
109
+ export function decodeAutoconfigToken(
110
+ token: string
111
+ ):
112
+ | { version: "v1"; content: AutoConfigTokenContentV1 }
113
+ | { version: "v2"; content: AutoConfigTokenContentV2 } {
114
+ if (token.startsWith(AUTOCONFIG_TOKEN_PREFIX_V1)) {
115
+ return { version: "v1", content: decodeAutoconfigTokenV1(token) };
116
+ }
117
+ if (token.startsWith(AUTOCONFIG_TOKEN_PREFIX_V2)) {
118
+ return { version: "v2", content: decodeAutoconfigTokenV2(token) };
119
+ }
120
+ throw new AutoConfigError(UNSUPPORTED_TOKEN_VERSION);
121
+ }
122
+
73
123
  export class AutoConfig {
74
124
  private readonly appId: string;
75
- private readonly endpointId: string;
76
- private readonly endpointIn: EndpointIn;
77
125
  private readonly webhook: Webhook;
78
126
  private readonly requestCtx: SvixRequestContext;
127
+ private readonly endpointIn: EndpointIn;
128
+ private readonly endpointId?: string;
129
+ private readonly autoconfigId?: string;
79
130
 
80
131
  public constructor(token: string, endpoint: EndpointIn) {
81
- const content = decodeAutoconfigTokenV1(token);
132
+ const decoded = decodeAutoconfigToken(token);
133
+
82
134
  let webhook: Webhook;
83
135
  try {
84
- webhook = new Webhook(content.esec);
136
+ webhook = new Webhook(decoded.content.esec);
85
137
  } catch {
86
138
  throw new AutoConfigError();
87
139
  }
88
140
 
89
- this.appId = content.aid;
90
- this.endpointId = content.eid;
141
+ this.appId = decoded.content.aid;
91
142
  this.endpointIn = endpoint;
92
143
  this.webhook = webhook;
93
144
 
94
- const svix = new SvixInternal(content.tok, { serverUrl: content.surl });
145
+ if (decoded.version === "v1") {
146
+ this.endpointId = decoded.content.eid;
147
+ } else {
148
+ this.autoconfigId = decoded.content.sid;
149
+ }
150
+
151
+ const svix = new SvixInternal(decoded.content.tok, {
152
+ serverUrl: decoded.content.surl,
153
+ });
95
154
  this.requestCtx = svix.getRequestCtx();
96
155
  }
97
156
 
98
157
  public subscribe(): Promise<EndpointOut> {
99
- return new InternalEndpoint(this.requestCtx).autoConfig.update(
100
- this.appId,
101
- this.endpointId,
102
- {
103
- endpoint: this.endpointIn,
104
- }
105
- );
158
+ const endpoint = new InternalEndpoint(this.requestCtx);
159
+ if (this.autoconfigId != null) {
160
+ return endpoint.autoconfig.subscribe(
161
+ this.appId,
162
+ this.autoconfigId,
163
+ this.endpointIn
164
+ );
165
+ }
166
+ return endpoint.autoConfigDeprecated.update(this.appId, this.endpointId as string, {
167
+ endpoint: this.endpointIn,
168
+ });
106
169
  }
107
170
 
108
171
  public verify(