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

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 (109) hide show
  1. package/dist-bundle/index.mjs +69 -61
  2. package/package.json +9 -6
  3. package/runtime-sdk/dist/auth/client.d.ts +14 -0
  4. package/runtime-sdk/dist/auth/client.js +28 -0
  5. package/runtime-sdk/dist/blob/client.d.ts +39 -0
  6. package/runtime-sdk/dist/blob/client.js +148 -0
  7. package/runtime-sdk/dist/blob/client.test.d.ts +1 -0
  8. package/runtime-sdk/dist/blob/client.test.js +494 -0
  9. package/runtime-sdk/dist/browser/app.d.ts +142 -0
  10. package/runtime-sdk/dist/browser/app.js +256 -0
  11. package/runtime-sdk/dist/browser/app.test.d.ts +1 -0
  12. package/runtime-sdk/dist/browser/app.test.js +81 -0
  13. package/runtime-sdk/dist/browser/errors.d.ts +39 -0
  14. package/runtime-sdk/dist/browser/errors.js +265 -0
  15. package/runtime-sdk/dist/browser/errors.test.d.ts +1 -0
  16. package/runtime-sdk/dist/browser/errors.test.js +41 -0
  17. package/runtime-sdk/dist/browser.d.ts +5 -0
  18. package/runtime-sdk/dist/browser.js +5 -0
  19. package/runtime-sdk/dist/client.d.ts +16 -0
  20. package/runtime-sdk/dist/client.js +22 -0
  21. package/runtime-sdk/dist/cms/client.d.ts +39 -0
  22. package/runtime-sdk/dist/cms/client.js +56 -0
  23. package/runtime-sdk/dist/cms/client.test.d.ts +1 -0
  24. package/runtime-sdk/dist/cms/client.test.js +56 -0
  25. package/runtime-sdk/dist/cms/media.d.ts +10 -0
  26. package/runtime-sdk/dist/cms/media.js +113 -0
  27. package/runtime-sdk/dist/cms/media.test.d.ts +1 -0
  28. package/runtime-sdk/dist/cms/media.test.js +76 -0
  29. package/runtime-sdk/dist/cms/taxonomy.d.ts +8 -0
  30. package/runtime-sdk/dist/cms/taxonomy.js +77 -0
  31. package/runtime-sdk/dist/cms/taxonomy.test.d.ts +1 -0
  32. package/runtime-sdk/dist/cms/taxonomy.test.js +65 -0
  33. package/runtime-sdk/dist/data-form/client.d.ts +19 -0
  34. package/runtime-sdk/dist/data-form/client.js +118 -0
  35. package/runtime-sdk/dist/data-form/client.test.d.ts +1 -0
  36. package/runtime-sdk/dist/data-form/client.test.js +210 -0
  37. package/runtime-sdk/dist/data-form/contracts.d.ts +10 -0
  38. package/runtime-sdk/dist/data-form/contracts.js +33 -0
  39. package/runtime-sdk/dist/data-form/index.d.ts +3 -0
  40. package/runtime-sdk/dist/data-form/index.js +2 -0
  41. package/runtime-sdk/dist/data-form/types.d.ts +110 -0
  42. package/runtime-sdk/dist/data-form/types.js +1 -0
  43. package/runtime-sdk/dist/http.d.ts +27 -0
  44. package/runtime-sdk/dist/http.js +126 -0
  45. package/runtime-sdk/dist/index.d.ts +19 -0
  46. package/runtime-sdk/dist/index.js +14 -0
  47. package/runtime-sdk/dist/notification/client.d.ts +16 -0
  48. package/runtime-sdk/dist/notification/client.js +39 -0
  49. package/runtime-sdk/dist/notification/index.d.ts +1 -0
  50. package/runtime-sdk/dist/notification/index.js +1 -0
  51. package/runtime-sdk/dist/react/context.d.ts +18 -0
  52. package/runtime-sdk/dist/react/context.js +28 -0
  53. package/runtime-sdk/dist/react/hooks.d.ts +90 -0
  54. package/runtime-sdk/dist/react/hooks.js +172 -0
  55. package/runtime-sdk/dist/react.d.ts +2 -0
  56. package/runtime-sdk/dist/react.js +2 -0
  57. package/runtime-sdk/dist/session/browser.d.ts +25 -0
  58. package/runtime-sdk/dist/session/browser.js +60 -0
  59. package/runtime-sdk/dist/types/auth.d.ts +38 -0
  60. package/runtime-sdk/dist/types/auth.js +1 -0
  61. package/runtime-sdk/dist/types/cms.d.ts +93 -0
  62. package/runtime-sdk/dist/types/cms.js +1 -0
  63. package/runtime-sdk/dist/types/notification.d.ts +37 -0
  64. package/runtime-sdk/dist/types/notification.js +1 -0
  65. package/runtime-sdk/dist/types/object-storage.d.ts +58 -0
  66. package/runtime-sdk/dist/types/object-storage.js +1 -0
  67. package/runtime-sdk/dist/types/workflow.d.ts +52 -0
  68. package/runtime-sdk/dist/types/workflow.js +1 -0
  69. package/runtime-sdk/dist/workflow/client.d.ts +30 -0
  70. package/runtime-sdk/dist/workflow/client.js +45 -0
  71. package/scaffold-assets-manifest.json +429 -0
  72. package/template-blocks/assets.ts +95 -0
  73. package/template-blocks/contract/capabilities.ts +33 -0
  74. package/template-blocks/contract/facade-contract.ts +59 -0
  75. package/template-blocks/contract/index.ts +4 -0
  76. package/template-blocks/contract/path-builders.ts +76 -0
  77. package/template-blocks/contract/route-path-patterns-source.ts +222 -0
  78. package/template-blocks/contract/route-path-patterns.ts +210 -0
  79. package/template-blocks/contract/runtime-config.ts +256 -0
  80. package/template-blocks/dev-support/index.ts +2 -0
  81. package/template-blocks/dev-support/node/auth-shell.ts +31 -0
  82. package/template-blocks/dev-support/node/index.ts +3 -0
  83. package/template-blocks/dev-support/node/proxy.ts +133 -0
  84. package/template-blocks/dev-support/node/runtime-config.ts +163 -0
  85. package/template-blocks/dev-support/vite/auth-shell.ts +23 -0
  86. package/template-blocks/dev-support/vite/index.ts +3 -0
  87. package/template-blocks/dev-support/vite/proxy.ts +451 -0
  88. package/template-blocks/dev-support/vite/runtime-config.ts +107 -0
  89. package/template-blocks/legacy/app-core.ts +153 -0
  90. package/template-blocks/legacy/index.ts +10 -0
  91. package/template-blocks/legacy/lifecycle.ts +38 -0
  92. package/template-blocks/legacy/metadata.ts +59 -0
  93. package/template-blocks/legacy/pages.ts +156 -0
  94. package/template-blocks/legacy/react-lock.ts +1599 -0
  95. package/template-blocks/legacy/react.ts +823 -0
  96. package/template-blocks/legacy/tailwind.ts +1138 -0
  97. package/template-blocks/legacy/wrappers/auth.ts +105 -0
  98. package/template-blocks/legacy/wrappers/proxy.ts +250 -0
  99. package/template-blocks/legacy/wrappers/runtime.ts +535 -0
  100. package/template-blocks/protocol/auth-routes.ts +82 -0
  101. package/template-blocks/protocol/cms-routes.ts +81 -0
  102. package/template-blocks/protocol/index.ts +4 -0
  103. package/template-blocks/protocol/public-app-routes.ts +48 -0
  104. package/template-blocks/protocol/route-map.ts +75 -0
  105. package/template-blocks/routing.ts +62 -0
  106. package/template-blocks/server/html-injection.ts +45 -0
  107. package/template-blocks/server/proxy-routing.ts +65 -0
  108. package/template-blocks/server/request-context.ts +103 -0
  109. package/template-blocks/server/route-strip.ts +47 -0
@@ -0,0 +1,494 @@
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
+ });
@@ -0,0 +1,142 @@
1
+ import { type BrowserSessionStore, type BrowserSessionStoreOptions } from "../session/browser.js";
2
+ import { type RuntimeCmsMediaUrlOptions } from "../cms/media.js";
3
+ import type { RuntimeWorkflowActionResult, RuntimeWorkflowObjectState, RuntimeWorkflowStartResult, RuntimeWorkflowTaskList, RuntimeWorkflowTimelineItem } from "../types/workflow.js";
4
+ import type { RuntimeNotificationAnnouncements, RuntimeNotificationCommandResult, RuntimeNotificationInboxItemStatus, RuntimeNotificationInboxList, RuntimeNotificationUnreadCount } from "../types/notification.js";
5
+ import type { RuntimeCmsCategoryNode, RuntimeCmsCategoryTreeResponse, RuntimeCmsCollectionListResponse, RuntimeCmsCollectionMetaResponse, RuntimeCmsDeliveryListResponse, RuntimeCmsImageSet, RuntimeCmsItemDeliveryPayload } from "../types/cms.js";
6
+ import type { RuntimeEndUserAuthMe, RuntimeEndUserRoleRecord } from "../types/auth.js";
7
+ import type { RuntimeBlobHydrateResponse, RuntimeCompleteBlobUploadResult, RuntimeCreateBlobUploadSessionInput, RuntimeHydrateBlobRefsInput, RuntimeHydrateRecordBlobsInput } from "../types/object-storage.js";
8
+ export type BrowserRuntimeAppOptions = {
9
+ baseUrl: string;
10
+ appId: string;
11
+ env?: "preview" | "prod";
12
+ session?: BrowserSessionStore;
13
+ sessionOptions?: BrowserSessionStoreOptions;
14
+ authPath?: string;
15
+ fetchImpl?: typeof fetch;
16
+ };
17
+ export declare function createBrowserRuntimeApp(options: BrowserRuntimeAppOptions): {
18
+ sdk: import("../client.js").PlatformRuntimeSdk;
19
+ session: BrowserSessionStore;
20
+ buildSignInUrl(returnTo?: string): string;
21
+ resolveAccess(me: RuntimeEndUserAuthMe | null | undefined): import("./errors.js").BrowserRuntimeAccessResolution;
22
+ classifyError(error: unknown): import("./errors.js").BrowserRuntimeErrorResolution;
23
+ classifyWorkflowError(error: unknown, params?: {
24
+ operationLabel?: string;
25
+ }): import("./errors.js").BrowserWorkflowErrorResolution;
26
+ getErrorMessage(error: unknown, fallback?: string): string;
27
+ getWorkflowErrorMessage(error: unknown, fallback?: string, params?: {
28
+ operationLabel?: string;
29
+ }): string;
30
+ getCurrentEndUser(): Promise<RuntimeEndUserAuthMe>;
31
+ getCurrentEndUserRoles(): Promise<RuntimeEndUserRoleRecord[]>;
32
+ isSignedIn(): Promise<boolean>;
33
+ getMe(): Promise<RuntimeEndUserAuthMe>;
34
+ listMyRoles(): Promise<RuntimeEndUserRoleRecord[]>;
35
+ listMyTasks(args?: {
36
+ status?: string;
37
+ limit?: number;
38
+ cursor?: string;
39
+ }): Promise<RuntimeWorkflowTaskList>;
40
+ listMyNotifications(args?: {
41
+ status?: RuntimeNotificationInboxItemStatus;
42
+ inboxType?: "task_projection" | "notification" | "alert" | "announcement";
43
+ limit?: number;
44
+ }): Promise<RuntimeNotificationInboxList>;
45
+ getMyNotificationUnreadCount(): Promise<RuntimeNotificationUnreadCount>;
46
+ listMyAnnouncements(args?: {
47
+ limit?: number;
48
+ }): Promise<RuntimeNotificationAnnouncements>;
49
+ markNotificationRead(itemId: string): Promise<RuntimeNotificationCommandResult>;
50
+ dismissNotification(itemId: string): Promise<RuntimeNotificationCommandResult>;
51
+ getWorkflowObjectState(args: {
52
+ objectType: string;
53
+ objectId: string;
54
+ }): Promise<RuntimeWorkflowObjectState>;
55
+ startWorkflow(args: {
56
+ definitionKey: string;
57
+ objectType: string;
58
+ objectId: string;
59
+ payload?: Record<string, unknown>;
60
+ }): Promise<RuntimeWorkflowStartResult>;
61
+ performWorkflowAction(args: {
62
+ instanceId: string;
63
+ actionKey: string;
64
+ comment?: string;
65
+ payload?: Record<string, unknown>;
66
+ }): Promise<RuntimeWorkflowActionResult>;
67
+ listWorkflowTimeline(args: {
68
+ objectType: string;
69
+ objectId: string;
70
+ }): Promise<RuntimeWorkflowTimelineItem[]>;
71
+ listCmsItems(args: {
72
+ tenantId: string;
73
+ collection: string;
74
+ page?: number;
75
+ pageSize?: number;
76
+ categoryId?: string;
77
+ tags?: string[];
78
+ featured?: boolean;
79
+ sortField?: string;
80
+ sortDirection?: "asc" | "desc";
81
+ q?: string;
82
+ }): Promise<RuntimeCmsDeliveryListResponse>;
83
+ listCmsCollections(args: {
84
+ tenantId: string;
85
+ }): Promise<RuntimeCmsCollectionListResponse>;
86
+ uploadBlob(args: RuntimeCreateBlobUploadSessionInput & {
87
+ body: Blob | ArrayBuffer | ArrayBufferView;
88
+ sha256?: string;
89
+ }): Promise<RuntimeCompleteBlobUploadResult>;
90
+ uploadBlobFile(file: Blob & {
91
+ name?: string;
92
+ }, args?: Omit<RuntimeCreateBlobUploadSessionInput, "original_filename" | "content_type" | "size_bytes"> & {
93
+ original_filename?: string;
94
+ content_type?: string;
95
+ sha256?: string;
96
+ }): Promise<RuntimeCompleteBlobUploadResult>;
97
+ uploadBlobFiles(files: Array<Blob & {
98
+ name?: string;
99
+ }>, args?: Omit<RuntimeCreateBlobUploadSessionInput, "original_filename" | "content_type" | "size_bytes"> & {
100
+ original_filenames?: string[];
101
+ content_types?: string[];
102
+ sha256_list?: Array<string | undefined>;
103
+ }): Promise<RuntimeCompleteBlobUploadResult[]>;
104
+ hydrateBlobRefs(args: RuntimeHydrateBlobRefsInput): Promise<RuntimeBlobHydrateResponse>;
105
+ hydrateRecordBlobs(args: RuntimeHydrateRecordBlobsInput): Promise<RuntimeBlobHydrateResponse>;
106
+ getCmsCollectionMeta(args: {
107
+ tenantId: string;
108
+ collection: string;
109
+ }): Promise<RuntimeCmsCollectionMetaResponse>;
110
+ listCmsCategories(args: {
111
+ tenantId: string;
112
+ collection: string;
113
+ }): Promise<RuntimeCmsCategoryTreeResponse>;
114
+ listCmsCategoryNodes(args: {
115
+ tenantId: string;
116
+ collection: string;
117
+ }): Promise<RuntimeCmsCategoryNode[]>;
118
+ getCmsItemBySlug(args: {
119
+ tenantId: string;
120
+ collection: string;
121
+ slug: string;
122
+ }): Promise<RuntimeCmsItemDeliveryPayload>;
123
+ getCmsItemById(args: {
124
+ tenantId: string;
125
+ collection: string;
126
+ itemId: string;
127
+ }): Promise<RuntimeCmsItemDeliveryPayload>;
128
+ resolveCmsImageSet(value: unknown, options?: RuntimeCmsMediaUrlOptions): RuntimeCmsImageSet | null;
129
+ resolveCmsImageUrl(value: unknown, options?: RuntimeCmsMediaUrlOptions): string | null;
130
+ resolveCmsCoverImageSet(item: RuntimeCmsItemDeliveryPayload, options?: RuntimeCmsMediaUrlOptions): RuntimeCmsImageSet | null;
131
+ resolveCmsCoverImageUrl(item: RuntimeCmsItemDeliveryPayload, options?: RuntimeCmsMediaUrlOptions): string | null;
132
+ buildCmsCategoryNameMap(source: RuntimeCmsCollectionMetaResponse | RuntimeCmsCategoryNode[] | Map<string, string> | null | undefined): Map<string, string>;
133
+ resolveCmsCategoryName(categoryId: unknown, source: RuntimeCmsCollectionMetaResponse | RuntimeCmsCategoryNode[] | Map<string, string> | null | undefined, fallback?: string): string;
134
+ resolveCmsItemCategoryName(item: RuntimeCmsItemDeliveryPayload, source: RuntimeCmsCollectionMetaResponse | RuntimeCmsCategoryNode[] | Map<string, string> | null | undefined, fallback?: string): string;
135
+ resolveCmsTagLabels(value: unknown): string[];
136
+ resolveCmsItemTagLabels(item: RuntimeCmsItemDeliveryPayload): string[];
137
+ signOut(): Promise<void>;
138
+ ensureAuthenticated(args?: {
139
+ returnTo?: string;
140
+ redirectToSignIn?: boolean;
141
+ }): Promise<RuntimeEndUserAuthMe | null>;
142
+ };