@voobase/cli 0.1.1-nonprod.2050 → 0.1.1-nonprod.2051

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voobase/cli",
3
- "version": "0.1.1-nonprod.2050",
3
+ "version": "0.1.1-nonprod.2051",
4
4
  "private": false,
5
5
  "platformDistributionProfile": {
6
6
  "name": "nonprod",
@@ -56,10 +56,10 @@
56
56
  "bundleDependencies": [],
57
57
  "platformDistributionBuild": {
58
58
  "channel": "nonprod",
59
- "version": "0.1.1-nonprod.2050",
60
- "sequence": 2050,
61
- "gitSha": "d865074",
62
- "fullGitSha": "d865074bb73db991855af0e2f8f722e88db00655",
63
- "buildId": "0.1.1-nonprod.2050+d865074"
59
+ "version": "0.1.1-nonprod.2051",
60
+ "sequence": 2051,
61
+ "gitSha": "af1febe",
62
+ "fullGitSha": "af1febe76cef4519c4ed2150bdde045a61ff0390",
63
+ "buildId": "0.1.1-nonprod.2051+af1febe"
64
64
  }
65
65
  }
@@ -17,14 +17,6 @@
17
17
  "path": "runtime-sdk/dist/blob/client.js",
18
18
  "sha256": "1cb565130c86cb00bf061192f4f0578e1aa484d074e4fe8cc07761f5c7dfa804"
19
19
  },
20
- {
21
- "path": "runtime-sdk/dist/blob/client.test.d.ts",
22
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
23
- },
24
- {
25
- "path": "runtime-sdk/dist/blob/client.test.js",
26
- "sha256": "9bc87c7f3bde3b592f6c9763d4c2cd306bfaa9b315cdcd91c770624a8eaf16c5"
27
- },
28
20
  {
29
21
  "path": "runtime-sdk/dist/browser/app.d.ts",
30
22
  "sha256": "e2244f9eaef5fa85e57ff751b299d040a6684c1500c62984d1f855c396cbdfcc"
@@ -33,14 +25,6 @@
33
25
  "path": "runtime-sdk/dist/browser/app.js",
34
26
  "sha256": "5807bfd1f85ca0e12b9c51c51de3dcd528d7504f2a45fa3729ce0bd57dfa05da"
35
27
  },
36
- {
37
- "path": "runtime-sdk/dist/browser/app.test.d.ts",
38
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
39
- },
40
- {
41
- "path": "runtime-sdk/dist/browser/app.test.js",
42
- "sha256": "dcfde97965c4e83260c45a3d8858f40633cc22cc7a43c09743429720747b28c4"
43
- },
44
28
  {
45
29
  "path": "runtime-sdk/dist/browser/errors.d.ts",
46
30
  "sha256": "520957c33f841e06f2282df3c711ad244cca666d724461c8e7accb2379e76a77"
@@ -49,14 +33,6 @@
49
33
  "path": "runtime-sdk/dist/browser/errors.js",
50
34
  "sha256": "107994491c044dca0a32dd4dc3fa8483a8ae1eca3bfc3a7f4324937f6b55a488"
51
35
  },
52
- {
53
- "path": "runtime-sdk/dist/browser/errors.test.d.ts",
54
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
55
- },
56
- {
57
- "path": "runtime-sdk/dist/browser/errors.test.js",
58
- "sha256": "405c27819b9c4ceac348e992b6bb12ba8585a4bb9dcc68f0adcc2e1a9a793af7"
59
- },
60
36
  {
61
37
  "path": "runtime-sdk/dist/browser.d.ts",
62
38
  "sha256": "96249a2d870ec023319e93fab82a9872411b0cc83974844593e0794c99e056db"
@@ -81,14 +57,6 @@
81
57
  "path": "runtime-sdk/dist/cms/client.js",
82
58
  "sha256": "2565577ac5688d3c4040e85b12f7ab46768c3e501a3966afc14709af8dbecf7a"
83
59
  },
84
- {
85
- "path": "runtime-sdk/dist/cms/client.test.d.ts",
86
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
87
- },
88
- {
89
- "path": "runtime-sdk/dist/cms/client.test.js",
90
- "sha256": "0eeea33aa1ce35b17c9a4aaecbea94a3eb2a27bc6ae18f65af496b4e5a3762cb"
91
- },
92
60
  {
93
61
  "path": "runtime-sdk/dist/cms/media.d.ts",
94
62
  "sha256": "bdb71ff512c54e3745bab8999287331cd005a89ee59fff79dc846f4b745abf38"
@@ -97,14 +65,6 @@
97
65
  "path": "runtime-sdk/dist/cms/media.js",
98
66
  "sha256": "9e0bd1bd3eb60dd67eb10eb4bc0f437703eb12760fc8b1f4ba6792e2b1686812"
99
67
  },
100
- {
101
- "path": "runtime-sdk/dist/cms/media.test.d.ts",
102
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
103
- },
104
- {
105
- "path": "runtime-sdk/dist/cms/media.test.js",
106
- "sha256": "8ef2b7a3c4305e3db3bfc342b481c4f0eaa54ecbc73c38730fde93814d02f4bd"
107
- },
108
68
  {
109
69
  "path": "runtime-sdk/dist/cms/taxonomy.d.ts",
110
70
  "sha256": "0ebed3e3a4b69c44d73a01dca59bc154e49151368049829c3df5eca1e04dd294"
@@ -113,14 +73,6 @@
113
73
  "path": "runtime-sdk/dist/cms/taxonomy.js",
114
74
  "sha256": "e2876d800f2b58419b567b3697445665d230f49b4393f405c549cbe7cc2bd9cc"
115
75
  },
116
- {
117
- "path": "runtime-sdk/dist/cms/taxonomy.test.d.ts",
118
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
119
- },
120
- {
121
- "path": "runtime-sdk/dist/cms/taxonomy.test.js",
122
- "sha256": "f6692eee5fee7dc3e0220d16e5f5dc531cfd46b674c0d8e71f3c183befbe6f39"
123
- },
124
76
  {
125
77
  "path": "runtime-sdk/dist/data-form/client.d.ts",
126
78
  "sha256": "08491242d56b4d13c3ff535aa4a4fdca2e8262ed60e934276a51da21fce94e47"
@@ -129,14 +81,6 @@
129
81
  "path": "runtime-sdk/dist/data-form/client.js",
130
82
  "sha256": "214d44779586a05ac45a11957a19d2b9c88cdf3bf51f8c67c9d9c72e8290943d"
131
83
  },
132
- {
133
- "path": "runtime-sdk/dist/data-form/client.test.d.ts",
134
- "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
135
- },
136
- {
137
- "path": "runtime-sdk/dist/data-form/client.test.js",
138
- "sha256": "ed7a19518021972d8847f7dcb715c0da88d38e095acca0c5d45b554c20ae06a4"
139
- },
140
84
  {
141
85
  "path": "runtime-sdk/dist/data-form/contracts.d.ts",
142
86
  "sha256": "1bc6125f7a412ac74b0b0c9c21345b651f06b9ee6946cfed4abf482d3bf96054"
@@ -1 +0,0 @@
1
- export {};
@@ -1,494 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { PlatformRuntimeSdk } from "../client.js";
4
- test("runtime blob client hydrates explicit blob refs", async () => {
5
- const calls = [];
6
- const sdk = new PlatformRuntimeSdk({
7
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo",
8
- appId: "app-demo",
9
- fetchImpl: async (input, init) => {
10
- const url = String(input);
11
- calls.push({ url, init: init ?? {} });
12
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/blobs/hydrate") {
13
- return new Response(JSON.stringify({
14
- items: [
15
- {
16
- blob_id: "blob_demo",
17
- name: "cover.png",
18
- mime_type: "image/png",
19
- size_bytes: 1234,
20
- preview_url: "https://cdn.example.com/blob_demo",
21
- download_url: "https://cdn.example.com/blob_demo"
22
- }
23
- ]
24
- }), {
25
- status: 200,
26
- headers: { "Content-Type": "application/json" }
27
- });
28
- }
29
- throw new Error(`Unexpected fetch url: ${url}`);
30
- }
31
- });
32
- const response = await sdk.blob.hydrateBlobRefs({
33
- items: [{ blob_id: "blob_demo" }]
34
- });
35
- assert.equal(calls.length, 1);
36
- assert.equal(calls[0]?.init.method, "POST");
37
- assert.equal(new Headers(calls[0]?.init.headers).get("content-type"), "application/json");
38
- assert.deepEqual(JSON.parse(String(calls[0]?.init.body)), {
39
- items: [{ blob_id: "blob_demo" }]
40
- });
41
- assert.deepEqual(response, {
42
- items: [
43
- {
44
- blob_id: "blob_demo",
45
- name: "cover.png",
46
- mime_type: "image/png",
47
- size_bytes: 1234,
48
- preview_url: "https://cdn.example.com/blob_demo",
49
- download_url: "https://cdn.example.com/blob_demo"
50
- }
51
- ]
52
- });
53
- });
54
- test("runtime blob client hydrates record-scoped blob refs", async () => {
55
- const calls = [];
56
- const sdk = new PlatformRuntimeSdk({
57
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo",
58
- appId: "app-demo",
59
- fetchImpl: async (input, init) => {
60
- const url = String(input);
61
- calls.push({ url, init: init ?? {} });
62
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/blobs/hydrate") {
63
- return new Response(JSON.stringify({
64
- items: [
65
- {
66
- blob_id: "blob_demo",
67
- name: "cover.png",
68
- mime_type: "image/png",
69
- size_bytes: 1234,
70
- preview_url: "https://cdn.example.com/blob_demo",
71
- download_url: "https://cdn.example.com/blob_demo"
72
- }
73
- ]
74
- }), {
75
- status: 200,
76
- headers: { "Content-Type": "application/json" }
77
- });
78
- }
79
- throw new Error(`Unexpected fetch url: ${url}`);
80
- }
81
- });
82
- const response = await sdk.blob.hydrateRecordBlobs({
83
- resourceKey: "event_signup",
84
- recordId: "rec_demo"
85
- });
86
- assert.equal(calls.length, 1);
87
- assert.equal(calls[0]?.init.method, "POST");
88
- assert.equal(new Headers(calls[0]?.init.headers).get("content-type"), "application/json");
89
- assert.deepEqual(JSON.parse(String(calls[0]?.init.body)), {
90
- resource_key: "event_signup",
91
- record_id: "rec_demo"
92
- });
93
- assert.deepEqual(response, {
94
- items: [
95
- {
96
- blob_id: "blob_demo",
97
- name: "cover.png",
98
- mime_type: "image/png",
99
- size_bytes: 1234,
100
- preview_url: "https://cdn.example.com/blob_demo",
101
- download_url: "https://cdn.example.com/blob_demo"
102
- }
103
- ]
104
- });
105
- });
106
- test("runtime blob client uploads binary through upload-ticket plus create-put-complete flow", async () => {
107
- const calls = [];
108
- const sdk = new PlatformRuntimeSdk({
109
- baseUrl: "https://runtime.example.com",
110
- appId: "app-demo",
111
- getHeaders: () => ({ Authorization: "Bearer token-demo" }),
112
- fetchImpl: async (input, init) => {
113
- const url = String(input);
114
- calls.push({ url, init: init ?? {} });
115
- if (url === "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-ticket") {
116
- return new Response(JSON.stringify({
117
- upload_ticket: "tkt_demo",
118
- expires_at: "2099-05-05T12:00:00.000Z"
119
- }), {
120
- status: 201,
121
- headers: { "Content-Type": "application/json" }
122
- });
123
- }
124
- if (url === "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions") {
125
- return new Response(JSON.stringify({
126
- upload_session_id: "uls_demo",
127
- blob_id: "blob_demo",
128
- upload_url: "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions/uls_demo/content",
129
- method: "PUT",
130
- expires_at: "2026-05-05T12:00:00.000Z",
131
- headers: {
132
- "x-upload-token": "upload-token-demo"
133
- }
134
- }), {
135
- status: 201,
136
- headers: { "Content-Type": "application/json" }
137
- });
138
- }
139
- if (url === "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions/uls_demo/content") {
140
- return new Response(null, { status: 204 });
141
- }
142
- if (url === "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions/uls_demo/complete") {
143
- return new Response(JSON.stringify({
144
- upload_session_id: "uls_demo",
145
- blob_id: "blob_demo",
146
- status: "completed",
147
- ref: {
148
- blob_id: "blob_demo"
149
- }
150
- }), {
151
- status: 200,
152
- headers: { "Content-Type": "application/json" }
153
- });
154
- }
155
- throw new Error(`Unexpected fetch url: ${url}`);
156
- }
157
- });
158
- const result = await sdk.blob.upload({
159
- original_filename: "avatar.png",
160
- content_type: "image/png",
161
- size_bytes: 4,
162
- client_request_id: "req-demo",
163
- field_key: "avatar",
164
- body: new Uint8Array([1, 2, 3, 4])
165
- });
166
- assert.deepEqual(result, {
167
- upload_session_id: "uls_demo",
168
- blob_id: "blob_demo",
169
- status: "completed",
170
- ref: {
171
- blob_id: "blob_demo"
172
- }
173
- });
174
- assert.equal(calls.length, 4);
175
- const ticketCall = calls[0];
176
- assert.equal(ticketCall.init.method, "POST");
177
- assert.equal(new Headers(ticketCall.init.headers).get("authorization"), "Bearer token-demo");
178
- const createCall = calls[1];
179
- assert.equal(createCall.init.method, "POST");
180
- assert.equal(new Headers(createCall.init.headers).get("content-type"), "application/json");
181
- assert.equal(new Headers(createCall.init.headers).get("authorization"), "Ticket tkt_demo");
182
- assert.deepEqual(JSON.parse(String(createCall.init.body)), {
183
- original_filename: "avatar.png",
184
- content_type: "image/png",
185
- size_bytes: 4,
186
- client_request_id: "req-demo",
187
- field_key: "avatar"
188
- });
189
- const uploadCall = calls[2];
190
- assert.equal(uploadCall.init.method, "PUT");
191
- assert.equal(new Headers(uploadCall.init.headers).get("content-type"), "image/png");
192
- assert.equal(new Headers(uploadCall.init.headers).get("authorization"), "Ticket tkt_demo");
193
- assert.equal(new Headers(uploadCall.init.headers).get("x-upload-token"), "upload-token-demo");
194
- assert.equal(uploadCall.init.body instanceof Uint8Array, true);
195
- const completeCall = calls[3];
196
- assert.equal(completeCall.init.method, "POST");
197
- assert.equal(new Headers(completeCall.init.headers).get("content-type"), "application/json");
198
- assert.equal(new Headers(completeCall.init.headers).get("authorization"), "Ticket tkt_demo");
199
- assert.deepEqual(JSON.parse(String(completeCall.init.body)), {
200
- size_bytes: 4
201
- });
202
- });
203
- test("runtime blob client uploads multiple files sequentially", async () => {
204
- const createdBodies = [];
205
- let issuedTickets = 0;
206
- const sdk = new PlatformRuntimeSdk({
207
- baseUrl: "https://runtime.example.com",
208
- appId: "app-demo",
209
- fetchImpl: async (input, init) => {
210
- const url = String(input);
211
- if (url.endsWith("/blobs/upload-ticket")) {
212
- issuedTickets += 1;
213
- return new Response(JSON.stringify({
214
- upload_ticket: `tkt_${issuedTickets}`,
215
- expires_at: "2099-05-05T12:00:00.000Z"
216
- }), {
217
- status: 201,
218
- headers: { "Content-Type": "application/json" }
219
- });
220
- }
221
- if (url.endsWith("/blobs/upload-sessions")) {
222
- const body = JSON.parse(String(init?.body ?? "{}"));
223
- createdBodies.push(body);
224
- const index = createdBodies.length;
225
- return new Response(JSON.stringify({
226
- upload_session_id: `uls_${index}`,
227
- blob_id: `blob_${index}`,
228
- upload_url: `https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions/uls_${index}/content`,
229
- method: "PUT",
230
- expires_at: "2026-05-05T12:00:00.000Z"
231
- }), {
232
- status: 201,
233
- headers: { "Content-Type": "application/json" }
234
- });
235
- }
236
- if (url.includes("/content")) {
237
- return new Response(null, { status: 204 });
238
- }
239
- if (url.includes("/complete")) {
240
- const match = url.match(/uls_(\d+)/);
241
- const index = match?.[1] ?? "1";
242
- return new Response(JSON.stringify({
243
- upload_session_id: `uls_${index}`,
244
- blob_id: `blob_${index}`,
245
- status: "completed",
246
- ref: { blob_id: `blob_${index}` }
247
- }), {
248
- status: 200,
249
- headers: { "Content-Type": "application/json" }
250
- });
251
- }
252
- throw new Error(`Unexpected fetch url: ${url}`);
253
- }
254
- });
255
- const results = await sdk.blob.uploadFiles([
256
- new File([new Uint8Array([1])], "a.txt", { type: "text/plain" }),
257
- new File([new Uint8Array([2])], "b.txt", { type: "text/plain" })
258
- ]);
259
- assert.deepEqual(results.map((item) => item.ref.blob_id), ["blob_1", "blob_2"]);
260
- assert.deepEqual(createdBodies.map((item) => item.original_filename), ["a.txt", "b.txt"]);
261
- assert.equal(issuedTickets, 1);
262
- });
263
- test("runtime blob client refreshes expired upload ticket and retries once", async () => {
264
- const authorizationHeaders = [];
265
- let issuedTickets = 0;
266
- const sdk = new PlatformRuntimeSdk({
267
- baseUrl: "https://runtime.example.com",
268
- appId: "app-demo",
269
- fetchImpl: async (input, init) => {
270
- const url = String(input);
271
- if (url.endsWith("/blobs/upload-ticket")) {
272
- issuedTickets += 1;
273
- return new Response(JSON.stringify({
274
- upload_ticket: `tkt_${issuedTickets}`,
275
- expires_at: "2099-05-05T12:00:00.000Z"
276
- }), {
277
- status: 201,
278
- headers: { "Content-Type": "application/json" }
279
- });
280
- }
281
- if (url.endsWith("/blobs/upload-sessions")) {
282
- const authorization = new Headers(init?.headers).get("authorization") ?? "";
283
- authorizationHeaders.push(authorization);
284
- if (authorization === "Ticket tkt_1") {
285
- return new Response(JSON.stringify({
286
- error: { code: "ticket_invalid", message: "ticket expired" }
287
- }), {
288
- status: 401,
289
- headers: { "Content-Type": "application/json" }
290
- });
291
- }
292
- return new Response(JSON.stringify({
293
- upload_session_id: "uls_retry",
294
- blob_id: "blob_retry",
295
- upload_url: "https://runtime.example.com/api/public/apps/app-demo/blobs/upload-sessions/uls_retry/content",
296
- method: "PUT",
297
- expires_at: "2099-05-05T12:00:00.000Z"
298
- }), {
299
- status: 201,
300
- headers: { "Content-Type": "application/json" }
301
- });
302
- }
303
- if (url.endsWith("/content")) {
304
- return new Response(null, { status: 204 });
305
- }
306
- if (url.endsWith("/complete")) {
307
- return new Response(JSON.stringify({
308
- upload_session_id: "uls_retry",
309
- blob_id: "blob_retry",
310
- status: "completed",
311
- ref: { blob_id: "blob_retry" }
312
- }), {
313
- status: 200,
314
- headers: { "Content-Type": "application/json" }
315
- });
316
- }
317
- throw new Error(`Unexpected fetch url: ${url}`);
318
- }
319
- });
320
- const result = await sdk.blob.upload({
321
- original_filename: "retry.txt",
322
- content_type: "text/plain",
323
- size_bytes: 5,
324
- body: new Uint8Array([1, 2, 3, 4, 5])
325
- });
326
- assert.equal(result.ref.blob_id, "blob_retry");
327
- assert.equal(issuedTickets, 2);
328
- assert.deepEqual(authorizationHeaders, ["Ticket tkt_1", "Ticket tkt_2"]);
329
- });
330
- test("runtime blob client resolves relative upload_url against preview path base", async () => {
331
- const calls = [];
332
- const sdk = new PlatformRuntimeSdk({
333
- baseUrl: "https://preview.example.com/previews/app-demo/rel-123",
334
- appId: "app-demo",
335
- fetchImpl: async (input, init) => {
336
- const url = String(input);
337
- calls.push(url);
338
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-ticket")) {
339
- return new Response(JSON.stringify({
340
- upload_ticket: "tkt_preview",
341
- expires_at: "2099-05-05T12:00:00.000Z"
342
- }), {
343
- status: 201,
344
- headers: { "Content-Type": "application/json" }
345
- });
346
- }
347
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions")) {
348
- return new Response(JSON.stringify({
349
- upload_session_id: "uls_preview",
350
- blob_id: "blob_preview",
351
- upload_url: "/api/public/apps/app-demo/blobs/upload-sessions/uls_preview/content",
352
- method: "PUT",
353
- expires_at: "2099-05-05T12:00:00.000Z"
354
- }), {
355
- status: 201,
356
- headers: { "Content-Type": "application/json" }
357
- });
358
- }
359
- if (url.endsWith("/previews/app-demo/rel-123/api/public/apps/app-demo/blobs/upload-sessions/uls_preview/content")) {
360
- return new Response(null, { status: 204 });
361
- }
362
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions/uls_preview/complete")) {
363
- return new Response(JSON.stringify({
364
- upload_session_id: "uls_preview",
365
- blob_id: "blob_preview",
366
- status: "completed",
367
- ref: { blob_id: "blob_preview" }
368
- }), {
369
- status: 200,
370
- headers: { "Content-Type": "application/json" }
371
- });
372
- }
373
- throw new Error(`Unexpected fetch url: ${url}`);
374
- }
375
- });
376
- const result = await sdk.blob.upload({
377
- original_filename: "preview.txt",
378
- content_type: "text/plain",
379
- size_bytes: 7,
380
- body: new Uint8Array([1, 2, 3, 4, 5, 6, 7])
381
- });
382
- assert.equal(result.ref.blob_id, "blob_preview");
383
- assert.ok(calls.includes("https://preview.example.com/previews/app-demo/rel-123/api/public/apps/app-demo/blobs/upload-sessions/uls_preview/content"));
384
- });
385
- test("runtime blob client keeps absolute upload_url unchanged under host mode", async () => {
386
- const calls = [];
387
- const sdk = new PlatformRuntimeSdk({
388
- baseUrl: "https://prod.app.example.com",
389
- appId: "app-demo",
390
- fetchImpl: async (input) => {
391
- const url = String(input);
392
- calls.push(url);
393
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-ticket")) {
394
- return new Response(JSON.stringify({
395
- upload_ticket: "tkt_host",
396
- expires_at: "2099-05-05T12:00:00.000Z"
397
- }), {
398
- status: 201,
399
- headers: { "Content-Type": "application/json" }
400
- });
401
- }
402
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions")) {
403
- return new Response(JSON.stringify({
404
- upload_session_id: "uls_host",
405
- blob_id: "blob_host",
406
- upload_url: "https://uploads.example.com/runtime/uls_host/content",
407
- method: "PUT",
408
- expires_at: "2099-05-05T12:00:00.000Z"
409
- }), {
410
- status: 201,
411
- headers: { "Content-Type": "application/json" }
412
- });
413
- }
414
- if (url === "https://uploads.example.com/runtime/uls_host/content") {
415
- return new Response(null, { status: 204 });
416
- }
417
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions/uls_host/complete")) {
418
- return new Response(JSON.stringify({
419
- upload_session_id: "uls_host",
420
- blob_id: "blob_host",
421
- status: "completed",
422
- ref: { blob_id: "blob_host" }
423
- }), {
424
- status: 200,
425
- headers: { "Content-Type": "application/json" }
426
- });
427
- }
428
- throw new Error(`Unexpected fetch url: ${url}`);
429
- }
430
- });
431
- const result = await sdk.blob.upload({
432
- original_filename: "host.txt",
433
- content_type: "text/plain",
434
- size_bytes: 3,
435
- body: new Uint8Array([1, 2, 3])
436
- });
437
- assert.equal(result.ref.blob_id, "blob_host");
438
- assert.ok(calls.includes("https://uploads.example.com/runtime/uls_host/content"));
439
- });
440
- test("runtime blob client resolves relative upload_url against prod path base", async () => {
441
- let contentUrl = "";
442
- const sdk = new PlatformRuntimeSdk({
443
- baseUrl: "https://platform.example.com/apps/t-demo/app-demo",
444
- appId: "app-demo",
445
- fetchImpl: async (input) => {
446
- const url = String(input);
447
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-ticket")) {
448
- return new Response(JSON.stringify({
449
- upload_ticket: "tkt_prod_path",
450
- expires_at: "2099-05-05T12:00:00.000Z"
451
- }), {
452
- status: 201,
453
- headers: { "Content-Type": "application/json" }
454
- });
455
- }
456
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions")) {
457
- return new Response(JSON.stringify({
458
- upload_session_id: "uls_prod_path",
459
- blob_id: "blob_prod_path",
460
- upload_url: "/api/public/apps/app-demo/blobs/upload-sessions/uls_prod_path/content",
461
- method: "PUT",
462
- expires_at: "2099-05-05T12:00:00.000Z"
463
- }), {
464
- status: 201,
465
- headers: { "Content-Type": "application/json" }
466
- });
467
- }
468
- if (url.includes("/blobs/upload-sessions/uls_prod_path/content")) {
469
- contentUrl = url;
470
- return new Response(null, { status: 204 });
471
- }
472
- if (url.endsWith("/api/public/apps/app-demo/blobs/upload-sessions/uls_prod_path/complete")) {
473
- return new Response(JSON.stringify({
474
- upload_session_id: "uls_prod_path",
475
- blob_id: "blob_prod_path",
476
- status: "completed",
477
- ref: { blob_id: "blob_prod_path" }
478
- }), {
479
- status: 200,
480
- headers: { "Content-Type": "application/json" }
481
- });
482
- }
483
- throw new Error(`Unexpected fetch url: ${url}`);
484
- }
485
- });
486
- const result = await sdk.blob.upload({
487
- original_filename: "prod-path.txt",
488
- content_type: "text/plain",
489
- size_bytes: 2,
490
- body: new Uint8Array([1, 2])
491
- });
492
- assert.equal(result.ref.blob_id, "blob_prod_path");
493
- assert.equal(contentUrl, "https://platform.example.com/apps/t-demo/app-demo/api/public/apps/app-demo/blobs/upload-sessions/uls_prod_path/content");
494
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,81 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createBrowserRuntimeApp } from "./app.js";
4
- test("buildSignInUrl normalizes preview return target to app-internal path", () => {
5
- const originalWindow = globalThis.window;
6
- const location = {
7
- origin: "http://127.0.0.1.nip.io",
8
- pathname: "/previews/app-demo/rel-123/submit",
9
- search: "?draft=1",
10
- hash: "#section",
11
- assign() { },
12
- };
13
- globalThis.window = {
14
- location,
15
- };
16
- try {
17
- const app = createBrowserRuntimeApp({
18
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-123/",
19
- appId: "app-demo",
20
- authPath: "/previews/app-demo/rel-123/auth",
21
- fetchImpl: async () => new Response(),
22
- });
23
- const signInUrl = new URL(app.buildSignInUrl());
24
- assert.equal(signInUrl.pathname, "/previews/app-demo/rel-123/auth");
25
- assert.equal(signInUrl.searchParams.get("return_to"), "/submit?draft=1#section");
26
- }
27
- finally {
28
- globalThis.window = originalWindow;
29
- }
30
- });
31
- test("buildSignInUrl accepts full platform path and stores app-internal return target", () => {
32
- const originalWindow = globalThis.window;
33
- globalThis.window = {
34
- location: {
35
- origin: "http://127.0.0.1.nip.io",
36
- pathname: "/previews/app-demo/rel-123/",
37
- search: "",
38
- hash: "",
39
- assign() { },
40
- },
41
- };
42
- try {
43
- const app = createBrowserRuntimeApp({
44
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-123/",
45
- appId: "app-demo",
46
- authPath: "/previews/app-demo/rel-123/auth",
47
- fetchImpl: async () => new Response(),
48
- });
49
- const signInUrl = new URL(app.buildSignInUrl("/previews/app-demo/rel-123/my-submissions/rec-1?tab=edit"));
50
- assert.equal(signInUrl.searchParams.get("return_to"), "/my-submissions/rec-1?tab=edit");
51
- }
52
- finally {
53
- globalThis.window = originalWindow;
54
- }
55
- });
56
- test("buildSignInUrl keeps host-mode return target as regular path", () => {
57
- const originalWindow = globalThis.window;
58
- globalThis.window = {
59
- location: {
60
- origin: "https://app-demo.example.com",
61
- pathname: "/my-submissions",
62
- search: "",
63
- hash: "",
64
- assign() { },
65
- },
66
- };
67
- try {
68
- const app = createBrowserRuntimeApp({
69
- baseUrl: "https://app-demo.example.com/",
70
- appId: "app-demo",
71
- authPath: "/auth",
72
- fetchImpl: async () => new Response(),
73
- });
74
- const signInUrl = new URL(app.buildSignInUrl());
75
- assert.equal(signInUrl.pathname, "/auth");
76
- assert.equal(signInUrl.searchParams.get("return_to"), "/my-submissions");
77
- }
78
- finally {
79
- globalThis.window = originalWindow;
80
- }
81
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,41 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { RuntimeApiError } from "../http.js";
4
- import { classifyBrowserWorkflowError } from "./errors.js";
5
- test("classifyBrowserWorkflowError maps definition gaps to configuration guidance", () => {
6
- const error = new RuntimeApiError("workflow definition was not found", 404, "workflow_definition_not_found", {
7
- definition_key: "wf-demo"
8
- }, "req-demo-1");
9
- const resolved = classifyBrowserWorkflowError(error, {
10
- appId: "app-demo",
11
- operationLabel: "Workflow 启动"
12
- });
13
- assert.equal(resolved.workflowKind, "configuration");
14
- assert.equal(resolved.isConfigurationGap, true);
15
- assert.match(resolved.message, /Workflow 定义或绑定/);
16
- assert.match(resolved.recommendation ?? "", /platform workflow diagnose --app app-demo/);
17
- });
18
- test("classifyBrowserWorkflowError maps forbidden actions to role-binding guidance", () => {
19
- const error = new RuntimeApiError("principal is not allowed to perform this action", 403, "workflow_action_forbidden", {
20
- action_key: "approve"
21
- }, "req-demo-2");
22
- const resolved = classifyBrowserWorkflowError(error, {
23
- appId: "app-demo"
24
- });
25
- assert.equal(resolved.workflowKind, "permission");
26
- assert.equal(resolved.isPermissionGap, true);
27
- assert.match(resolved.message, /没有执行这个动作的权限/);
28
- assert.match(resolved.recommendation ?? "", /role binding/);
29
- });
30
- test("classifyBrowserWorkflowError maps already-active instances to state guidance", () => {
31
- const error = new RuntimeApiError("an active workflow instance already exists for this object", 409, "workflow_instance_already_active", {
32
- object_id: "demo-request-001"
33
- }, "req-demo-3");
34
- const resolved = classifyBrowserWorkflowError(error, {
35
- appId: "app-demo"
36
- });
37
- assert.equal(resolved.workflowKind, "state");
38
- assert.equal(resolved.isStateGap, true);
39
- assert.match(resolved.message, /已经有进行中的流程实例/);
40
- assert.match(resolved.recommendation ?? "", /刷新当前对象状态/);
41
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,56 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { PlatformRuntimeSdk } from "../client.js";
4
- test("runtime cms client keeps mounted preview path single when building requests", async () => {
5
- const calls = [];
6
- const sdk = new PlatformRuntimeSdk({
7
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/",
8
- appId: "app-demo",
9
- fetchImpl: async (input) => {
10
- const url = String(input);
11
- calls.push(url);
12
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/cms/collections?tenant_id=t-demo") {
13
- return new Response(JSON.stringify({ collections: [] }), {
14
- status: 200,
15
- headers: { "Content-Type": "application/json" }
16
- });
17
- }
18
- throw new Error(`Unexpected fetch url: ${url}`);
19
- }
20
- });
21
- const result = await sdk.cms.listCollections({ tenantId: "t-demo" });
22
- assert.deepEqual(result, { collections: [] });
23
- assert.deepEqual(calls, [
24
- "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/cms/collections?tenant_id=t-demo"
25
- ]);
26
- });
27
- test("runtime cms client forwards featured query when listing items", async () => {
28
- const calls = [];
29
- const sdk = new PlatformRuntimeSdk({
30
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/",
31
- appId: "app-demo",
32
- fetchImpl: async (input) => {
33
- const url = String(input);
34
- calls.push(url);
35
- if (url ===
36
- "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/cms/collections/article/items?tenant_id=t-demo&page=1&page_size=10&featured=true") {
37
- return new Response(JSON.stringify({ items: [], page: 1, page_size: 10, total: 0 }), {
38
- status: 200,
39
- headers: { "Content-Type": "application/json" }
40
- });
41
- }
42
- throw new Error(`Unexpected fetch url: ${url}`);
43
- }
44
- });
45
- const result = await sdk.cms.listItems({
46
- tenantId: "t-demo",
47
- collection: "article",
48
- page: 1,
49
- pageSize: 10,
50
- featured: true
51
- });
52
- assert.deepEqual(result, { items: [], page: 1, page_size: 10, total: 0 });
53
- assert.deepEqual(calls, [
54
- "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/cms/collections/article/items?tenant_id=t-demo&page=1&page_size=10&featured=true"
55
- ]);
56
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,76 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { normalizeCmsMediaUrl, resolveCmsCoverImageSet, resolveCmsCoverImageUrl, resolveCmsImageSet, resolveCmsImageUrl } from "./media.js";
4
- test("resolveCmsImageUrl prefers md variant and upgrades insecure http on https pages", () => {
5
- const url = resolveCmsImageUrl({
6
- original_url: "http://assets.example.com/original.jpg",
7
- variant_urls: {
8
- thumbnail: "http://assets.example.com/thumbnail.jpg",
9
- md: "http://assets.example.com/md.jpg",
10
- lg: "http://assets.example.com/lg.jpg"
11
- }
12
- }, { currentProtocol: "https:" });
13
- assert.equal(url, "https://assets.example.com/md.jpg");
14
- });
15
- test("resolveCmsImageSet supports direct string values", () => {
16
- assert.deepEqual(resolveCmsImageSet("http://assets.example.com/file.jpg"), {
17
- original: "http://assets.example.com/file.jpg",
18
- thumbnail: null,
19
- sm: null,
20
- md: null,
21
- lg: null
22
- });
23
- });
24
- test("resolveCmsCoverImageUrl prefers presentation cover_url before nested content fields", () => {
25
- const item = buildItem({
26
- presentation: {
27
- cover_url: "http://assets.example.com/presentation-md.jpg",
28
- cover_urls: {
29
- original: "http://assets.example.com/presentation-original.jpg",
30
- lg: "http://assets.example.com/presentation-lg.jpg"
31
- }
32
- },
33
- content: {
34
- cover: {
35
- original_url: "http://assets.example.com/content-original.jpg",
36
- variant_urls: {
37
- md: "http://assets.example.com/content-md.jpg"
38
- }
39
- }
40
- }
41
- });
42
- assert.equal(resolveCmsCoverImageUrl(item, { currentProtocol: "https:" }), "https://assets.example.com/presentation-md.jpg");
43
- });
44
- test("resolveCmsCoverImageSet falls back to nested cover field when presentation is absent", () => {
45
- const item = buildItem({
46
- content: {
47
- cover: {
48
- original_url: "http://assets.example.com/content-original.jpg",
49
- variant_urls: {
50
- thumbnail: "http://assets.example.com/content-thumbnail.jpg",
51
- md: "http://assets.example.com/content-md.jpg"
52
- }
53
- }
54
- }
55
- });
56
- assert.deepEqual(resolveCmsCoverImageSet(item, { upgradeInsecureHttp: true }), {
57
- original: "https://assets.example.com/content-original.jpg",
58
- thumbnail: "https://assets.example.com/content-thumbnail.jpg",
59
- sm: null,
60
- md: "https://assets.example.com/content-md.jpg",
61
- lg: null
62
- });
63
- });
64
- test("normalizeCmsMediaUrl upgrades only insecure http", () => {
65
- assert.equal(normalizeCmsMediaUrl("http://assets.example.com/a.jpg", { upgradeInsecureHttp: true }), "https://assets.example.com/a.jpg");
66
- assert.equal(normalizeCmsMediaUrl("https://assets.example.com/a.jpg", { upgradeInsecureHttp: true }), "https://assets.example.com/a.jpg");
67
- assert.equal(normalizeCmsMediaUrl("/relative/path.jpg", { upgradeInsecureHttp: true }), "/relative/path.jpg");
68
- });
69
- function buildItem(overrides) {
70
- return {
71
- collection: "articles",
72
- system: {},
73
- content: {},
74
- ...overrides
75
- };
76
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,65 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { buildCmsCategoryNameMap, resolveCmsCategoryName, resolveCmsItemCategoryName, resolveCmsItemTagLabels, resolveCmsTagLabels } from "./taxonomy.js";
4
- test("buildCmsCategoryNameMap accepts collection meta and keeps id fallback", () => {
5
- const meta = {
6
- collection: "article",
7
- label: "Article",
8
- template: "article",
9
- published_item_count: 2,
10
- schema: {
11
- schema_version: "1",
12
- kind: "collection",
13
- name: "article",
14
- label: "Article",
15
- description: "",
16
- template: "article",
17
- capabilities: {},
18
- fields: []
19
- },
20
- categories: [
21
- buildCategory({ id: "cat-a", name: "设计资讯" }),
22
- buildCategory({ id: "cat-b", name: "" })
23
- ]
24
- };
25
- assert.deepEqual(Array.from(buildCmsCategoryNameMap(meta).entries()), [
26
- ["cat-a", "设计资讯"],
27
- ["cat-b", "cat-b"]
28
- ]);
29
- });
30
- test("resolveCmsItemCategoryName falls back to category id or default label", () => {
31
- const categories = [buildCategory({ id: "cat-a", name: "设计资讯" })];
32
- assert.equal(resolveCmsCategoryName("cat-a", categories), "设计资讯");
33
- assert.equal(resolveCmsCategoryName("cat-missing", categories), "cat-missing");
34
- assert.equal(resolveCmsItemCategoryName(buildItem({ system: { category_id: null } }), categories), "未分类");
35
- });
36
- test("resolveCmsTagLabels trims, splits and humanizes tag values", () => {
37
- assert.deepEqual(resolveCmsTagLabels(["featured_post", "design-news", " Featured Post ", "", null]), [
38
- "featured post",
39
- "design news"
40
- ]);
41
- assert.deepEqual(resolveCmsTagLabels("alpha,beta,gamma"), ["alpha", "beta", "gamma"]);
42
- });
43
- test("resolveCmsItemTagLabels returns normalized tag labels from item system", () => {
44
- const labels = resolveCmsItemTagLabels(buildItem({ system: { tags: ["ui_ux", "design-news"] } }));
45
- assert.deepEqual(labels, ["ui ux", "design news"]);
46
- });
47
- function buildCategory(overrides) {
48
- return {
49
- id: "cat-1",
50
- collection: "article",
51
- name: "默认分类",
52
- slug: "default",
53
- parent_id: null,
54
- order: 0,
55
- ...overrides
56
- };
57
- }
58
- function buildItem(overrides) {
59
- return {
60
- collection: "article",
61
- system: {},
62
- content: {},
63
- ...overrides
64
- };
65
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,210 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { readFile } from "node:fs/promises";
3
- import test from "node:test";
4
- import { RuntimeDataFormClient } from "./client.js";
5
- test("runtime data form client keeps mounted preview path single when building requests", async () => {
6
- const calls = [];
7
- const client = new RuntimeDataFormClient({
8
- baseUrl: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/",
9
- appId: "app-demo",
10
- fetchImpl: async (input, init) => {
11
- const url = String(input);
12
- calls.push({ url, method: String(init?.method ?? "GET") });
13
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources?env=preview") {
14
- return jsonResponse({ resources: [] });
15
- }
16
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/schema?env=preview") {
17
- return jsonResponse({
18
- resource: {
19
- resource_key: "profile",
20
- display_name: "Profile",
21
- resource_kind: "end_user_owned",
22
- owner_type: "identity",
23
- sharing_mode: "app_scoped",
24
- access_policy: {
25
- create: "own",
26
- read: "own",
27
- update: "own"
28
- },
29
- fields: []
30
- }
31
- });
32
- }
33
- if (url ===
34
- "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/records?page=2&page_size=5&owner_scope=all&q=demo&env=preview") {
35
- return jsonResponse({
36
- items: [],
37
- page: 2,
38
- page_size: 5,
39
- total: 0,
40
- has_more: false
41
- });
42
- }
43
- if (url === "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/records?env=preview") {
44
- return jsonResponse({
45
- record_id: "rec_1",
46
- resource_key: "profile",
47
- owner_identity_id: "id_1",
48
- values: { nickname: "neo" },
49
- created_at: "2026-05-15T00:00:00.000Z",
50
- updated_at: "2026-05-15T00:00:00.000Z"
51
- }, 201);
52
- }
53
- throw new Error(`Unexpected fetch url: ${url}`);
54
- }
55
- });
56
- const resources = await client.listResources();
57
- const schema = await client.getSchema("profile");
58
- const list = await client.listRecords({ resourceKey: "profile", page: 2, pageSize: 5, ownerScope: "all", q: " demo " });
59
- const created = await client.createRecord("profile", {
60
- values: {
61
- nickname: "neo"
62
- }
63
- });
64
- assert.deepEqual(resources, { resources: [] });
65
- assert.equal(schema.resource.resource_key, "profile");
66
- assert.equal(list.page, 2);
67
- assert.equal(created.record_id, "rec_1");
68
- assert.deepEqual(calls, [
69
- {
70
- url: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources?env=preview",
71
- method: "GET"
72
- },
73
- {
74
- url: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/schema?env=preview",
75
- method: "GET"
76
- },
77
- {
78
- url: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/records?page=2&page_size=5&owner_scope=all&q=demo&env=preview",
79
- method: "GET"
80
- },
81
- {
82
- url: "http://127.0.0.1.nip.io/previews/app-demo/rel-demo/api/public/apps/app-demo/data-form/resources/profile/records?env=preview",
83
- method: "POST"
84
- }
85
- ]);
86
- });
87
- function jsonResponse(payload, status = 200) {
88
- return new Response(JSON.stringify(payload), {
89
- status,
90
- headers: { "Content-Type": "application/json" }
91
- });
92
- }
93
- test("runtime data form client prefers explicit env instead of inferring prod from localhost base url", async () => {
94
- const calls = [];
95
- const client = new RuntimeDataFormClient({
96
- baseUrl: "http://127.0.0.1:3003/",
97
- appId: "app-demo",
98
- env: "preview",
99
- fetchImpl: async (input) => {
100
- const url = String(input);
101
- calls.push(url);
102
- if (url === "http://127.0.0.1:3003/api/public/apps/app-demo/data-form/resources?env=preview") {
103
- return jsonResponse({ resources: [] });
104
- }
105
- throw new Error(`Unexpected fetch url: ${url}`);
106
- }
107
- });
108
- await client.listResources();
109
- assert.deepEqual(calls, [
110
- "http://127.0.0.1:3003/api/public/apps/app-demo/data-form/resources?env=preview"
111
- ]);
112
- });
113
- test("runtime data form client infers preview from tenant-scoped preview base url", async () => {
114
- const calls = [];
115
- const client = new RuntimeDataFormClient({
116
- baseUrl: "https://runtime.example.com/previews/t-jason-workspace/app-demo/rel-123/",
117
- appId: "app-demo",
118
- fetchImpl: async (input) => {
119
- const url = String(input);
120
- calls.push(url);
121
- if (url === "https://runtime.example.com/previews/t-jason-workspace/app-demo/rel-123/api/public/apps/app-demo/data-form/resources?env=preview") {
122
- return jsonResponse({ resources: [] });
123
- }
124
- throw new Error(`Unexpected fetch url: ${url}`);
125
- }
126
- });
127
- await client.listResources();
128
- assert.deepEqual(calls, [
129
- "https://runtime.example.com/previews/t-jason-workspace/app-demo/rel-123/api/public/apps/app-demo/data-form/resources?env=preview"
130
- ]);
131
- });
132
- test("runtime data form client infers preview from explicit publicBaseUrl when baseUrl is request origin", async () => {
133
- const calls = [];
134
- const client = new RuntimeDataFormClient({
135
- baseUrl: "https://runtime.example.com/",
136
- publicBaseUrl: "https://runtime.example.com/previews/t-jason-workspace/app-demo/rel-123/",
137
- appId: "app-demo",
138
- fetchImpl: async (input) => {
139
- const url = String(input);
140
- calls.push(url);
141
- if (url === "https://runtime.example.com/api/public/apps/app-demo/data-form/resources?env=preview") {
142
- return jsonResponse({ resources: [] });
143
- }
144
- throw new Error(`Unexpected fetch url: ${url}`);
145
- }
146
- });
147
- await client.listResources();
148
- assert.deepEqual(calls, [
149
- "https://runtime.example.com/api/public/apps/app-demo/data-form/resources?env=preview"
150
- ]);
151
- });
152
- test("runtime data form client infers preview from routed preview base url", async () => {
153
- const calls = [];
154
- const client = new RuntimeDataFormClient({
155
- baseUrl: "https://runtime.example.com/previews/r/t-jason-workspace/app-demo/rel-123/",
156
- appId: "app-demo",
157
- fetchImpl: async (input) => {
158
- const url = String(input);
159
- calls.push(url);
160
- if (url === "https://runtime.example.com/previews/r/t-jason-workspace/app-demo/rel-123/api/public/apps/app-demo/data-form/resources?env=preview") {
161
- return jsonResponse({ resources: [] });
162
- }
163
- throw new Error(`Unexpected fetch url: ${url}`);
164
- }
165
- });
166
- await client.listResources();
167
- assert.deepEqual(calls, [
168
- "https://runtime.example.com/previews/r/t-jason-workspace/app-demo/rel-123/api/public/apps/app-demo/data-form/resources?env=preview"
169
- ]);
170
- });
171
- test("runtime data form client falls back to browser runtime config when options only carry request origin", async () => {
172
- const previousWindow = globalThis.window;
173
- globalThis.window = {
174
- __PLATFORM_RUNTIME_CONFIG__: {
175
- publicBaseUrl: "https://runtime.example.com/previews/r/t-jason-workspace/app-demo/rel-123/",
176
- mode: "preview"
177
- }
178
- };
179
- try {
180
- const calls = [];
181
- const client = new RuntimeDataFormClient({
182
- baseUrl: "https://runtime.example.com/",
183
- appId: "app-demo",
184
- fetchImpl: async (input) => {
185
- const url = String(input);
186
- calls.push(url);
187
- if (url === "https://runtime.example.com/api/public/apps/app-demo/data-form/resources?env=preview") {
188
- return jsonResponse({ resources: [] });
189
- }
190
- throw new Error(`Unexpected fetch url: ${url}`);
191
- }
192
- });
193
- await client.listResources();
194
- assert.deepEqual(calls, [
195
- "https://runtime.example.com/api/public/apps/app-demo/data-form/resources?env=preview"
196
- ]);
197
- }
198
- finally {
199
- if (previousWindow === undefined) {
200
- delete globalThis.window;
201
- }
202
- else {
203
- globalThis.window = previousWindow;
204
- }
205
- }
206
- });
207
- test("compiled browser data form client is self-contained for vendored templates", async () => {
208
- const compiled = await readFile(new URL("../../dist/data-form/client.js", import.meta.url), "utf8");
209
- assert.doesNotMatch(compiled, /@platform\/url-contract/u);
210
- });