rivetkit 2.0.34 → 2.0.36
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/dist/tsup/actor/errors.cjs +6 -2
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/actor/errors.d.cts +7 -1
- package/dist/tsup/actor/errors.d.ts +7 -1
- package/dist/tsup/actor/errors.js +5 -1
- package/dist/tsup/{chunk-6KPMUCTX.js → chunk-2YHR67M4.js} +26 -2
- package/dist/tsup/{chunk-6KPMUCTX.js.map → chunk-2YHR67M4.js.map} +1 -1
- package/dist/tsup/{chunk-3I3NOVWF.cjs → chunk-65O3MUPO.cjs} +9 -9
- package/dist/tsup/{chunk-3I3NOVWF.cjs.map → chunk-65O3MUPO.cjs.map} +1 -1
- package/dist/tsup/{chunk-PNDDMBWF.cjs → chunk-6TLJAB64.cjs} +223 -269
- package/dist/tsup/chunk-6TLJAB64.cjs.map +1 -0
- package/dist/tsup/{chunk-ZNFKB5PN.cjs → chunk-7EGXCVJL.cjs} +55 -48
- package/dist/tsup/chunk-7EGXCVJL.cjs.map +1 -0
- package/dist/tsup/{chunk-US7J2C4A.js → chunk-D35UUQOR.js} +2 -2
- package/dist/tsup/{chunk-GQ5VRA2G.js → chunk-DSPZ7BZN.js} +96 -6
- package/dist/tsup/chunk-DSPZ7BZN.js.map +1 -0
- package/dist/tsup/{chunk-JXQJHBUL.js → chunk-EO5JABFU.js} +47 -36
- package/dist/tsup/chunk-EO5JABFU.js.map +1 -0
- package/dist/tsup/{chunk-2PDSCMHY.js → chunk-KJLJLNHR.js} +273 -319
- package/dist/tsup/chunk-KJLJLNHR.js.map +1 -0
- package/dist/tsup/{chunk-IKLDUHPA.cjs → chunk-OYJU4B2I.cjs} +130 -119
- package/dist/tsup/chunk-OYJU4B2I.cjs.map +1 -0
- package/dist/tsup/{chunk-ULWSFIPU.cjs → chunk-RTSLQIZ5.cjs} +355 -265
- package/dist/tsup/chunk-RTSLQIZ5.cjs.map +1 -0
- package/dist/tsup/{chunk-FWT2BTVT.cjs → chunk-SNAUKDDK.cjs} +26 -2
- package/dist/tsup/chunk-SNAUKDDK.cjs.map +1 -0
- package/dist/tsup/{chunk-VXUKLCT5.js → chunk-UDKWYSU3.js} +7 -3
- package/dist/tsup/chunk-UDKWYSU3.js.map +1 -0
- package/dist/tsup/{chunk-VGG36TGE.cjs → chunk-VIUUUBXQ.cjs} +3 -3
- package/dist/tsup/{chunk-VGG36TGE.cjs.map → chunk-VIUUUBXQ.cjs.map} +1 -1
- package/dist/tsup/{chunk-LD52TCZK.cjs → chunk-WRYYREBN.cjs} +13 -9
- package/dist/tsup/chunk-WRYYREBN.cjs.map +1 -0
- package/dist/tsup/{chunk-YLUTGXVY.js → chunk-X5GKJWTG.js} +12 -5
- package/dist/tsup/{chunk-YLUTGXVY.js.map → chunk-X5GKJWTG.js.map} +1 -1
- package/dist/tsup/{chunk-BDE7AKE7.js → chunk-Z5CSXFVY.js} +5 -5
- package/dist/tsup/client/mod.cjs +6 -6
- package/dist/tsup/client/mod.d.cts +8 -8
- package/dist/tsup/client/mod.d.ts +8 -8
- package/dist/tsup/client/mod.js +5 -5
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{config-BybOFjaz.d.cts → config--NjwiYlS.d.cts} +24 -25
- package/dist/tsup/{config-B-whEERh.d.ts → config-CRuzI6n4.d.ts} +24 -25
- package/dist/tsup/{driver-DqaHKTyo.d.ts → driver-BcmckRaF.d.ts} +1 -1
- package/dist/tsup/driver-helpers/mod.cjs +4 -4
- package/dist/tsup/driver-helpers/mod.d.cts +2 -2
- package/dist/tsup/driver-helpers/mod.d.ts +2 -2
- package/dist/tsup/driver-helpers/mod.js +3 -3
- package/dist/tsup/driver-test-suite/mod.cjs +37 -37
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +2 -2
- package/dist/tsup/driver-test-suite/mod.d.ts +2 -2
- package/dist/tsup/driver-test-suite/mod.js +9 -9
- package/dist/tsup/{driver-CcjuH3oe.d.cts → driver-yKjYx9Yy.d.cts} +1 -1
- package/dist/tsup/mod.cjs +8 -8
- package/dist/tsup/mod.d.cts +4 -4
- package/dist/tsup/mod.d.ts +4 -4
- package/dist/tsup/mod.js +7 -7
- package/dist/tsup/test/mod.cjs +8 -8
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +7 -7
- package/dist/tsup/utils.cjs +3 -3
- package/dist/tsup/utils.js +2 -2
- package/package.json +3 -3
- package/src/actor/errors.ts +24 -0
- package/src/actor/router.ts +13 -1
- package/src/client/config.ts +54 -48
- package/src/common/router.ts +12 -0
- package/src/drivers/engine/actor-driver.ts +2 -1
- package/src/drivers/engine/config.ts +6 -3
- package/src/globals.d.ts +1 -0
- package/src/manager/router.ts +8 -1
- package/src/registry/config/index.ts +98 -90
- package/src/registry/config/serverless.ts +34 -3
- package/src/remote-manager-driver/api-utils.ts +3 -0
- package/src/serverless/router.ts +24 -2
- package/src/utils/endpoint-parser.test.ts +184 -171
- package/src/utils/endpoint-parser.ts +70 -65
- package/src/utils/env-vars.ts +6 -0
- package/dist/tsup/chunk-2PDSCMHY.js.map +0 -1
- package/dist/tsup/chunk-FWT2BTVT.cjs.map +0 -1
- package/dist/tsup/chunk-GQ5VRA2G.js.map +0 -1
- package/dist/tsup/chunk-IKLDUHPA.cjs.map +0 -1
- package/dist/tsup/chunk-JXQJHBUL.js.map +0 -1
- package/dist/tsup/chunk-LD52TCZK.cjs.map +0 -1
- package/dist/tsup/chunk-PNDDMBWF.cjs.map +0 -1
- package/dist/tsup/chunk-ULWSFIPU.cjs.map +0 -1
- package/dist/tsup/chunk-VXUKLCT5.js.map +0 -1
- package/dist/tsup/chunk-ZNFKB5PN.cjs.map +0 -1
- /package/dist/tsup/{chunk-US7J2C4A.js.map → chunk-D35UUQOR.js.map} +0 -0
- /package/dist/tsup/{chunk-BDE7AKE7.js.map → chunk-Z5CSXFVY.js.map} +0 -0
|
@@ -1,189 +1,202 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { tryParseEndpoint } from "./endpoint-parser";
|
|
4
|
+
|
|
5
|
+
// Helper to create a mock Zod refinement context for testing
|
|
6
|
+
function createMockCtx(): { ctx: z.RefinementCtx; issues: z.ZodIssue[] } {
|
|
7
|
+
const issues: z.ZodIssue[] = [];
|
|
8
|
+
const ctx = {
|
|
9
|
+
addIssue: (issue: z.IssueData) => {
|
|
10
|
+
issues.push(issue as z.ZodIssue);
|
|
11
|
+
},
|
|
12
|
+
path: [],
|
|
13
|
+
value: undefined,
|
|
14
|
+
issues: [],
|
|
15
|
+
} as unknown as z.RefinementCtx;
|
|
16
|
+
return { ctx, issues };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("tryParseEndpoint", () => {
|
|
20
|
+
describe("basic parsing", () => {
|
|
21
|
+
test("parses endpoint with full auth", () => {
|
|
22
|
+
const { ctx, issues } = createMockCtx();
|
|
23
|
+
const result = tryParseEndpoint(ctx, {
|
|
24
|
+
endpoint: "https://foo:bar@api.rivet.dev",
|
|
25
|
+
});
|
|
26
|
+
expect(issues).toHaveLength(0);
|
|
27
|
+
expect(result).toEqual({
|
|
28
|
+
endpoint: "https://api.rivet.dev/",
|
|
29
|
+
namespace: "foo",
|
|
30
|
+
token: "bar",
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("parses endpoint with namespace only", () => {
|
|
35
|
+
const { ctx, issues } = createMockCtx();
|
|
36
|
+
const result = tryParseEndpoint(ctx, {
|
|
37
|
+
endpoint: "https://foo@api.rivet.dev",
|
|
38
|
+
});
|
|
39
|
+
expect(issues).toHaveLength(0);
|
|
40
|
+
expect(result).toEqual({
|
|
41
|
+
endpoint: "https://api.rivet.dev/",
|
|
42
|
+
namespace: "foo",
|
|
43
|
+
token: undefined,
|
|
44
|
+
});
|
|
31
45
|
});
|
|
32
46
|
|
|
33
|
-
test("throws on fragment", () => {
|
|
34
|
-
expect(() =>
|
|
35
|
-
zodParseEndpoint("https://foo:bar@api.rivet.dev#section"),
|
|
36
|
-
).toThrow("endpoint cannot contain a fragment");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("handles percent-encoded characters in namespace", () => {
|
|
40
|
-
const result = zodParseEndpoint("https://foo%40bar:token@api.rivet.dev");
|
|
41
|
-
expect(result.endpoint).toBe("https://api.rivet.dev/");
|
|
42
|
-
expect(result.namespace).toBe("foo@bar");
|
|
43
|
-
expect(result.token).toBe("token");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("handles percent-encoded characters in token", () => {
|
|
47
|
-
const result = zodParseEndpoint("https://foo:bar%3Abaz@api.rivet.dev");
|
|
48
|
-
expect(result.endpoint).toBe("https://api.rivet.dev/");
|
|
49
|
-
expect(result.namespace).toBe("foo");
|
|
50
|
-
expect(result.token).toBe("bar:baz");
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe("namespace only (no token)", () => {
|
|
55
|
-
test("parses namespace without token", () => {
|
|
56
|
-
const result = zodParseEndpoint("https://foo@api.rivet.dev");
|
|
57
|
-
expect(result.endpoint).toBe("https://api.rivet.dev/");
|
|
58
|
-
expect(result.namespace).toBe("foo");
|
|
59
|
-
expect(result.token).toBeUndefined();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("parses namespace without token with path", () => {
|
|
63
|
-
const result = zodParseEndpoint("https://foo@api.rivet.dev/v1/actors");
|
|
64
|
-
expect(result.endpoint).toBe("https://api.rivet.dev/v1/actors");
|
|
65
|
-
expect(result.namespace).toBe("foo");
|
|
66
|
-
expect(result.token).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("no auth", () => {
|
|
71
47
|
test("parses endpoint without auth", () => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
expect(result.endpoint).toBe("http://localhost:6420/");
|
|
96
|
-
expect(result.namespace).toBe("foo");
|
|
97
|
-
expect(result.token).toBe("bar");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("parses http endpoint without auth", () => {
|
|
101
|
-
const result = zodParseEndpoint("http://localhost:6420");
|
|
102
|
-
expect(result.endpoint).toBe("http://localhost:6420/");
|
|
103
|
-
expect(result.namespace).toBeUndefined();
|
|
104
|
-
expect(result.token).toBeUndefined();
|
|
48
|
+
const { ctx, issues } = createMockCtx();
|
|
49
|
+
const result = tryParseEndpoint(ctx, {
|
|
50
|
+
endpoint: "https://api.rivet.dev",
|
|
51
|
+
});
|
|
52
|
+
expect(issues).toHaveLength(0);
|
|
53
|
+
expect(result).toEqual({
|
|
54
|
+
endpoint: "https://api.rivet.dev/",
|
|
55
|
+
namespace: undefined,
|
|
56
|
+
token: undefined,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("preserves path", () => {
|
|
61
|
+
const { ctx, issues } = createMockCtx();
|
|
62
|
+
const result = tryParseEndpoint(ctx, {
|
|
63
|
+
endpoint: "https://foo:bar@api.rivet.dev/v1/actors",
|
|
64
|
+
});
|
|
65
|
+
expect(issues).toHaveLength(0);
|
|
66
|
+
expect(result).toEqual({
|
|
67
|
+
endpoint: "https://api.rivet.dev/v1/actors",
|
|
68
|
+
namespace: "foo",
|
|
69
|
+
token: "bar",
|
|
70
|
+
});
|
|
105
71
|
});
|
|
106
72
|
});
|
|
107
73
|
|
|
108
|
-
describe("
|
|
109
|
-
test("
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
expect(
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("
|
|
118
|
-
|
|
74
|
+
describe("validation errors", () => {
|
|
75
|
+
test("adds issue for invalid URL", () => {
|
|
76
|
+
const { ctx, issues } = createMockCtx();
|
|
77
|
+
const result = tryParseEndpoint(ctx, { endpoint: "not-a-url" });
|
|
78
|
+
expect(result).toBeUndefined();
|
|
79
|
+
expect(issues).toHaveLength(1);
|
|
80
|
+
expect(issues[0]?.message).toContain("invalid URL");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("adds issue for query string", () => {
|
|
84
|
+
const { ctx, issues } = createMockCtx();
|
|
85
|
+
const result = tryParseEndpoint(ctx, {
|
|
86
|
+
endpoint: "https://foo:bar@api.rivet.dev?region=us",
|
|
87
|
+
});
|
|
88
|
+
expect(result).toBeUndefined();
|
|
89
|
+
expect(issues).toHaveLength(1);
|
|
90
|
+
expect(issues[0]?.message).toBe("endpoint cannot contain a query string");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("adds issue for fragment", () => {
|
|
94
|
+
const { ctx, issues } = createMockCtx();
|
|
95
|
+
const result = tryParseEndpoint(ctx, {
|
|
96
|
+
endpoint: "https://foo:bar@api.rivet.dev#section",
|
|
97
|
+
});
|
|
98
|
+
expect(result).toBeUndefined();
|
|
99
|
+
expect(issues).toHaveLength(1);
|
|
100
|
+
expect(issues[0]?.message).toBe("endpoint cannot contain a fragment");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("adds issue for token without namespace", () => {
|
|
104
|
+
const { ctx, issues } = createMockCtx();
|
|
105
|
+
const result = tryParseEndpoint(ctx, {
|
|
106
|
+
endpoint: "https://:token@api.rivet.dev",
|
|
107
|
+
});
|
|
108
|
+
expect(result).toBeUndefined();
|
|
109
|
+
expect(issues).toHaveLength(1);
|
|
110
|
+
expect(issues[0]?.message).toBe(
|
|
119
111
|
"endpoint cannot have a token without a namespace",
|
|
120
112
|
);
|
|
121
113
|
});
|
|
122
114
|
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe("EndpointSchema", () => {
|
|
126
|
-
test("parses endpoint with full auth", () => {
|
|
127
|
-
const result = EndpointSchema.parse("https://foo:bar@api.rivet.dev");
|
|
128
|
-
expect(result).toEqual({
|
|
129
|
-
endpoint: "https://api.rivet.dev/",
|
|
130
|
-
namespace: "foo",
|
|
131
|
-
token: "bar",
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
115
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
116
|
+
describe("duplicate credential checking", () => {
|
|
117
|
+
test("adds issue when namespace in URL and config", () => {
|
|
118
|
+
const { ctx, issues } = createMockCtx();
|
|
119
|
+
const result = tryParseEndpoint(ctx, {
|
|
120
|
+
endpoint: "https://url-ns@api.rivet.dev",
|
|
121
|
+
path: ["endpoint"],
|
|
122
|
+
namespace: "config-ns",
|
|
123
|
+
});
|
|
124
|
+
// Still returns result, but adds issue
|
|
125
|
+
expect(result).toEqual({
|
|
126
|
+
endpoint: "https://api.rivet.dev/",
|
|
127
|
+
namespace: "url-ns",
|
|
128
|
+
token: undefined,
|
|
129
|
+
});
|
|
130
|
+
expect(issues).toHaveLength(1);
|
|
131
|
+
expect(issues[0]?.message).toContain(
|
|
132
|
+
"cannot specify namespace both in endpoint URL and as a separate config option",
|
|
133
|
+
);
|
|
134
|
+
expect(issues[0]?.path).toEqual(["namespace"]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("adds issue when token in URL and config", () => {
|
|
138
|
+
const { ctx, issues } = createMockCtx();
|
|
139
|
+
const result = tryParseEndpoint(ctx, {
|
|
140
|
+
endpoint: "https://ns:url-token@api.rivet.dev",
|
|
141
|
+
path: ["endpoint"],
|
|
142
|
+
token: "config-token",
|
|
143
|
+
});
|
|
144
|
+
// Still returns result, but adds issue
|
|
145
|
+
expect(result).toEqual({
|
|
146
|
+
endpoint: "https://api.rivet.dev/",
|
|
147
|
+
namespace: "ns",
|
|
148
|
+
token: "url-token",
|
|
149
|
+
});
|
|
150
|
+
expect(issues).toHaveLength(1);
|
|
151
|
+
expect(issues[0]?.message).toContain(
|
|
152
|
+
"cannot specify token both in endpoint URL and as a separate config option",
|
|
153
|
+
);
|
|
154
|
+
expect(issues[0]?.path).toEqual(["token"]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("adds issues for both namespace and token duplicates", () => {
|
|
158
|
+
const { ctx, issues } = createMockCtx();
|
|
159
|
+
const result = tryParseEndpoint(ctx, {
|
|
160
|
+
endpoint: "https://url-ns:url-token@api.rivet.dev",
|
|
161
|
+
path: ["endpoint"],
|
|
162
|
+
namespace: "config-ns",
|
|
163
|
+
token: "config-token",
|
|
164
|
+
});
|
|
165
|
+
expect(result).toBeDefined();
|
|
166
|
+
expect(issues).toHaveLength(2);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("no issue when namespace only in URL", () => {
|
|
170
|
+
const { ctx, issues } = createMockCtx();
|
|
171
|
+
const result = tryParseEndpoint(ctx, {
|
|
172
|
+
endpoint: "https://url-ns@api.rivet.dev",
|
|
173
|
+
path: ["endpoint"],
|
|
174
|
+
token: "config-token",
|
|
175
|
+
});
|
|
176
|
+
expect(result).toBeDefined();
|
|
177
|
+
expect(issues).toHaveLength(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("no issue when namespace only in config", () => {
|
|
181
|
+
const { ctx, issues } = createMockCtx();
|
|
182
|
+
const result = tryParseEndpoint(ctx, {
|
|
183
|
+
endpoint: "https://api.rivet.dev",
|
|
184
|
+
path: ["endpoint"],
|
|
185
|
+
namespace: "config-ns",
|
|
186
|
+
});
|
|
187
|
+
expect(result).toBeDefined();
|
|
188
|
+
expect(issues).toHaveLength(0);
|
|
161
189
|
});
|
|
162
190
|
});
|
|
163
191
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
EndpointSchema.parse("https://foo:bar@api.rivet.dev#section"),
|
|
173
|
-
).toThrow();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("throws on invalid URL", () => {
|
|
177
|
-
expect(() => EndpointSchema.parse("not-a-url")).toThrow();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("works with optional()", () => {
|
|
181
|
-
const schema = EndpointSchema.optional();
|
|
182
|
-
expect(schema.parse(undefined)).toBeUndefined();
|
|
183
|
-
expect(schema.parse("https://foo:bar@api.rivet.dev")).toEqual({
|
|
184
|
-
endpoint: "https://api.rivet.dev/",
|
|
185
|
-
namespace: "foo",
|
|
186
|
-
token: "bar",
|
|
192
|
+
describe("custom path", () => {
|
|
193
|
+
test("uses custom path in error issues", () => {
|
|
194
|
+
const { ctx, issues } = createMockCtx();
|
|
195
|
+
tryParseEndpoint(ctx, {
|
|
196
|
+
endpoint: "not-a-url",
|
|
197
|
+
path: ["serverless", "publicEndpoint"],
|
|
198
|
+
});
|
|
199
|
+
expect(issues[0]?.path).toEqual(["serverless", "publicEndpoint"]);
|
|
187
200
|
});
|
|
188
201
|
});
|
|
189
202
|
});
|
|
@@ -6,9 +6,22 @@ export interface ParsedEndpoint {
|
|
|
6
6
|
token: string | undefined;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface TryParseEndpointOptions {
|
|
10
|
+
/** The endpoint URL string to parse */
|
|
11
|
+
endpoint: string;
|
|
12
|
+
/** Path prefix for error messages (default: ["endpoint"]) */
|
|
13
|
+
path?: (string | number)[];
|
|
14
|
+
/** Namespace from config, to check for duplicate specification */
|
|
15
|
+
namespace?: string;
|
|
16
|
+
/** Token from config, to check for duplicate specification */
|
|
17
|
+
token?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
/**
|
|
10
21
|
* Parses an endpoint URL that may contain auth syntax for namespace and token.
|
|
11
22
|
*
|
|
23
|
+
* Uses ctx.addIssue for clean error reporting in Zod transforms.
|
|
24
|
+
*
|
|
12
25
|
* Supports formats like:
|
|
13
26
|
* - `https://namespace:token@api.rivet.dev`
|
|
14
27
|
* - `https://namespace@api.rivet.dev` (namespace only, no token)
|
|
@@ -17,87 +30,68 @@ export interface ParsedEndpoint {
|
|
|
17
30
|
*
|
|
18
31
|
* Query strings and fragments are not allowed as they may conflict with
|
|
19
32
|
* runtime parameters.
|
|
33
|
+
*
|
|
34
|
+
* @param ctx - Zod refinement context for error reporting
|
|
35
|
+
* @param options - Parsing options including endpoint, path, and config values
|
|
36
|
+
* @returns ParsedEndpoint on success, undefined on error (after adding issues to ctx)
|
|
20
37
|
*/
|
|
21
|
-
export function
|
|
38
|
+
export function tryParseEndpoint(
|
|
39
|
+
ctx: z.RefinementCtx,
|
|
40
|
+
options: TryParseEndpointOptions,
|
|
41
|
+
): ParsedEndpoint | undefined {
|
|
42
|
+
const { endpoint, path = ["endpoint"], namespace: configNamespace, token: configToken } = options;
|
|
22
43
|
// Parse the URL
|
|
23
|
-
|
|
44
|
+
let url: URL;
|
|
45
|
+
try {
|
|
46
|
+
url = new URL(endpoint);
|
|
47
|
+
} catch {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
message: `invalid URL: ${endpoint}`,
|
|
51
|
+
path,
|
|
52
|
+
});
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
24
55
|
|
|
25
56
|
// Reject query strings
|
|
26
57
|
if (url.search) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
]);
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: "custom",
|
|
60
|
+
message: "endpoint cannot contain a query string",
|
|
61
|
+
path,
|
|
62
|
+
});
|
|
63
|
+
return undefined;
|
|
34
64
|
}
|
|
35
65
|
|
|
36
66
|
// Reject fragments
|
|
37
67
|
if (url.hash) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
]);
|
|
68
|
+
ctx.addIssue({
|
|
69
|
+
code: "custom",
|
|
70
|
+
message: "endpoint cannot contain a fragment",
|
|
71
|
+
path,
|
|
72
|
+
});
|
|
73
|
+
return undefined;
|
|
45
74
|
}
|
|
46
75
|
|
|
47
76
|
// Extract namespace and token from username and password
|
|
48
77
|
// URL stores these as percent-encoded, so we need to decode them
|
|
49
|
-
const namespace = url.username
|
|
78
|
+
const namespace = url.username
|
|
79
|
+
? decodeURIComponent(url.username)
|
|
80
|
+
: undefined;
|
|
50
81
|
const token = url.password ? decodeURIComponent(url.password) : undefined;
|
|
51
82
|
|
|
52
83
|
// Reject token without namespace (e.g., https://:token@api.rivet.dev)
|
|
53
84
|
if (token && !namespace) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
]);
|
|
85
|
+
ctx.addIssue({
|
|
86
|
+
code: "custom",
|
|
87
|
+
message: "endpoint cannot have a token without a namespace",
|
|
88
|
+
path,
|
|
89
|
+
});
|
|
90
|
+
return undefined;
|
|
61
91
|
}
|
|
62
92
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
url.password = "";
|
|
66
|
-
|
|
67
|
-
// Get the cleaned endpoint without auth
|
|
68
|
-
const cleanedEndpoint = url.toString();
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
endpoint: cleanedEndpoint,
|
|
72
|
-
namespace,
|
|
73
|
-
token,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Zod schema that parses an endpoint URL string and extracts namespace/token from HTTP auth syntax.
|
|
79
|
-
*
|
|
80
|
-
* Input: `"https://namespace:token@api.rivet.dev/path"`
|
|
81
|
-
* Output: `{ endpoint: "https://api.rivet.dev/path", namespace: "namespace", token: "token" }`
|
|
82
|
-
*/
|
|
83
|
-
export const EndpointSchema = z.string().transform((endpoint): ParsedEndpoint => {
|
|
84
|
-
return zodParseEndpoint(endpoint);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
export type EndpointSchemaInput = z.input<typeof EndpointSchema>;
|
|
88
|
-
export type EndpointSchemaOutput = z.output<typeof EndpointSchema>;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Zod refinement that validates namespace/token aren't specified both in the endpoint URL
|
|
92
|
-
* and as separate config options.
|
|
93
|
-
*/
|
|
94
|
-
export function zodCheckDuplicateCredentials(
|
|
95
|
-
resolvedEndpoint: ParsedEndpoint,
|
|
96
|
-
config: { namespace?: string; token?: string },
|
|
97
|
-
ctx: z.RefinementCtx,
|
|
98
|
-
): void {
|
|
99
|
-
// Check if endpoint contains namespace but namespace is also specified in config
|
|
100
|
-
if (resolvedEndpoint.namespace && config.namespace) {
|
|
93
|
+
// Check for duplicate credentials (specified both in URL and config)
|
|
94
|
+
if (namespace && configNamespace) {
|
|
101
95
|
ctx.addIssue({
|
|
102
96
|
code: "custom",
|
|
103
97
|
message:
|
|
@@ -105,9 +99,7 @@ export function zodCheckDuplicateCredentials(
|
|
|
105
99
|
path: ["namespace"],
|
|
106
100
|
});
|
|
107
101
|
}
|
|
108
|
-
|
|
109
|
-
// Check if endpoint contains token but token is also specified in config
|
|
110
|
-
if (resolvedEndpoint.token && config.token) {
|
|
102
|
+
if (token && configToken) {
|
|
111
103
|
ctx.addIssue({
|
|
112
104
|
code: "custom",
|
|
113
105
|
message:
|
|
@@ -115,5 +107,18 @@ export function zodCheckDuplicateCredentials(
|
|
|
115
107
|
path: ["token"],
|
|
116
108
|
});
|
|
117
109
|
}
|
|
110
|
+
|
|
111
|
+
// Strip auth from the URL by clearing username and password
|
|
112
|
+
url.username = "";
|
|
113
|
+
url.password = "";
|
|
114
|
+
|
|
115
|
+
// Get the cleaned endpoint without auth
|
|
116
|
+
const cleanedEndpoint = url.toString();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
endpoint: cleanedEndpoint,
|
|
120
|
+
namespace,
|
|
121
|
+
token,
|
|
122
|
+
};
|
|
118
123
|
}
|
|
119
124
|
|
package/src/utils/env-vars.ts
CHANGED
|
@@ -29,6 +29,12 @@ export const getRivetRunnerVersion = (): number | undefined => {
|
|
|
29
29
|
const value = getEnvUniversal("RIVET_RUNNER_VERSION");
|
|
30
30
|
return value !== undefined ? parseInt(value, 10) : undefined;
|
|
31
31
|
};
|
|
32
|
+
export const getRivetPublicEndpoint = (): string | undefined =>
|
|
33
|
+
getEnvUniversal("RIVET_PUBLIC_ENDPOINT");
|
|
34
|
+
export const getRivetPublicToken = (): string | undefined =>
|
|
35
|
+
getEnvUniversal("RIVET_PUBLIC_TOKEN");
|
|
36
|
+
// There is no RIVET_PUBLIC_NAMESPACE because the frontend and backend cannot
|
|
37
|
+
// use different namespaces
|
|
32
38
|
|
|
33
39
|
// RivetKit configuration
|
|
34
40
|
export const getRivetkitInspectorToken = (): string | undefined =>
|