busabase-sdk 0.60.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/airapp.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as BusabaseClient } from "./client-SAiQFoug.js";
1
+ import { t as BusabaseClient } from "./client-DUJ67kpK.js";
2
2
  //#region src/airapp.d.ts
3
3
  type NodeChangeRequestInput = Parameters<BusabaseClient["nodes"]["createChangeRequest"]>[0];
4
4
  type NodeOperationInput = NodeChangeRequestInput["operations"][number];
@@ -215,13 +215,37 @@ declare function inspectProvisionedResources(client: AirAppProvisioningClient, c
215
215
  */
216
216
  declare function provisionDeclaredResources(client: AirAppProvisioningClient, config: AirAppResourceConfig): Promise<AirAppResources>;
217
217
  /** The client surface `publishAirApp` needs, on top of provisioning. */
218
- type AirAppPublishClient = AirAppProvisioningClient & Pick<BusabaseClient, "fileTrees" | "changeRequests">;
219
- /** One file of the app's built AirApp bundle, as `publishAirApp` receives it. */
220
- interface AirAppFileInput {
218
+ type AirAppPublishClient = AirAppProvisioningClient & Pick<BusabaseClient, "fileTrees" | "changeRequests" | "assets">;
219
+ /**
220
+ * One file of the app's built AirApp bundle, as `publishAirApp` receives it.
221
+ *
222
+ * A file tree holds text inline but binaries as Assets — the server never
223
+ * stores image bytes in a file operation, it stores an `assetId` pointing at
224
+ * the Asset library. So a bundle that ships a favicon or an empty-state
225
+ * illustration needs a shape that can say "these are bytes", and until now
226
+ * this type could only say `content: string`. Callers did the only thing that
227
+ * type allowed: read the PNG as UTF-8 and publish the mojibake. That fails
228
+ * silently — the publish succeeds and the image is simply broken.
229
+ *
230
+ * `bytes` is resolved to an `assetId` by `resolveAirAppFileAssets`, which
231
+ * `publishAirApp` calls for you.
232
+ */
233
+ type AirAppFileInput = {
221
234
  path: string;
222
- content: string;
223
235
  mimeType?: string;
224
- }
236
+ } & ({
237
+ content: string;
238
+ bytes?: never;
239
+ assetId?: never;
240
+ } | {
241
+ bytes: Uint8Array;
242
+ content?: never;
243
+ assetId?: never;
244
+ } | {
245
+ assetId: string;
246
+ content?: never;
247
+ bytes?: never;
248
+ });
225
249
  /**
226
250
  * What one publish did. `merged` is the question a caller actually has — did
227
251
  * this bundle go live, or is it waiting for someone?
@@ -258,6 +282,16 @@ type AirAppPublishResult = {
258
282
  * makes for a Base's fields (`additiveFieldsFor` only ever appends).
259
283
  */
260
284
  declare function buildAirAppFileOperations(localFiles: AirAppFileInput[], deployedPaths: Iterable<string>): FileTreeCreateOrUpdateOperation[];
285
+ /**
286
+ * Upload every `bytes` file in a bundle and hand back the same list with
287
+ * `assetId` in their place. Text files pass through untouched — a bundle with
288
+ * no binaries makes no upload calls at all.
289
+ *
290
+ * Separate from `buildAirAppFileOperations` so that function can stay pure and
291
+ * directly testable; separate from `publishAirApp` so a caller assembling its
292
+ * own file operations (a Skill or Drive sync, say) can reuse it.
293
+ */
294
+ declare function resolveAirAppFileAssets(client: AirAppPublishClient, files: AirAppFileInput[], fetchImpl?: typeof fetch): Promise<AirAppFileInput[]>;
261
295
  /**
262
296
  * Publish the app's own AirApp bundle: create it under the Folder when this
263
297
  * Space has never had it, or propose the local files as an update when it
@@ -280,4 +314,4 @@ declare function buildAirAppFileOperations(localFiles: AirAppFileInput[], deploy
280
314
  */
281
315
  declare function publishAirApp(client: AirAppPublishClient, config: AirAppResourceConfig, files: AirAppFileInput[]): Promise<AirAppPublishResult>;
282
316
  //#endregion
283
- export { AirAppBaseDeclaration, AirAppFieldDeclaration, AirAppFileInput, AirAppFolderDeclaration, AirAppNodeDeclaration, AirAppOwnershipRepair, AirAppProvisionedBase, AirAppProvisioningClient, AirAppPublishClient, AirAppPublishResult, AirAppResourceConfig, AirAppResourceOwnership, AirAppResources, AirAppSetupCode, AirAppSetupError, buildAirAppFileOperations, buildProvisionOperations, inspectProvisionedResources, isNotFound, provisionDeclaredResources, publishAirApp, resolveProvisionedFolder };
317
+ export { AirAppBaseDeclaration, AirAppFieldDeclaration, AirAppFileInput, AirAppFolderDeclaration, AirAppNodeDeclaration, AirAppOwnershipRepair, AirAppProvisionedBase, AirAppProvisioningClient, AirAppPublishClient, AirAppPublishResult, AirAppResourceConfig, AirAppResourceOwnership, AirAppResources, AirAppSetupCode, AirAppSetupError, buildAirAppFileOperations, buildProvisionOperations, inspectProvisionedResources, isNotFound, provisionDeclaredResources, publishAirApp, resolveAirAppFileAssets, resolveProvisionedFolder };
package/dist/airapp.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as APP_ROOT_RESOURCE_KEY } from "./template-D10kjpd-.js";
1
+ import { i as uploadAsset, t as APP_ROOT_RESOURCE_KEY } from "./template-DRMp6tKv.js";
2
2
  //#region src/airapp.ts
3
3
  /**
4
4
  * AirApp resource provisioning — how an app claims (or creates) the Folder and
@@ -402,12 +402,50 @@ function provisionDeclaredResources(client, config) {
402
402
  */
403
403
  function buildAirAppFileOperations(localFiles, deployedPaths) {
404
404
  const deployed = new Set(deployedPaths);
405
- return localFiles.map((file) => ({
406
- kind: deployed.has(file.path) ? "update" : "create",
407
- path: file.path,
408
- content: file.content,
409
- ...file.mimeType ? { mimeType: file.mimeType } : {}
410
- }));
405
+ return localFiles.map((file) => {
406
+ if (file.bytes) throw new Error(`buildAirAppFileOperations: ${file.path} carries bytes — run resolveAirAppFileAssets first to turn them into an assetId`);
407
+ return {
408
+ kind: deployed.has(file.path) ? "update" : "create",
409
+ path: file.path,
410
+ ...file.assetId ? { assetId: file.assetId } : { content: file.content },
411
+ ...file.mimeType ? { mimeType: file.mimeType } : {}
412
+ };
413
+ });
414
+ }
415
+ /**
416
+ * Upload every `bytes` file in a bundle and hand back the same list with
417
+ * `assetId` in their place. Text files pass through untouched — a bundle with
418
+ * no binaries makes no upload calls at all.
419
+ *
420
+ * Separate from `buildAirAppFileOperations` so that function can stay pure and
421
+ * directly testable; separate from `publishAirApp` so a caller assembling its
422
+ * own file operations (a Skill or Drive sync, say) can reuse it.
423
+ */
424
+ async function resolveAirAppFileAssets(client, files, fetchImpl) {
425
+ const resolved = [];
426
+ for (const file of files) {
427
+ if (!file.bytes) {
428
+ resolved.push(file);
429
+ continue;
430
+ }
431
+ const mimeType = file.mimeType ?? "application/octet-stream";
432
+ const asset = await uploadAsset(client, file.bytes, {
433
+ fileName: basenameOf(file.path),
434
+ mimeType
435
+ }, fetchImpl);
436
+ if (!asset.assetId) throw new Error(`resolveAirAppFileAssets: ${file.path} uploaded but this host returned no assetId, so no file can reference it`);
437
+ resolved.push({
438
+ path: file.path,
439
+ assetId: asset.assetId,
440
+ mimeType
441
+ });
442
+ }
443
+ return resolved;
444
+ }
445
+ /** Last path segment — file trees always use POSIX separators. */
446
+ function basenameOf(filePath) {
447
+ const segments = filePath.split("/");
448
+ return segments[segments.length - 1] || filePath;
411
449
  }
412
450
  /**
413
451
  * Find a still-pending ChangeRequest that already proposes creating this
@@ -461,6 +499,7 @@ async function publishAirApp(client, config, files) {
461
499
  if (!airApp) throw setupError("SETUP_CONFLICT", "This app does not declare an airApp to publish");
462
500
  const current = await inspectProvisionedResources(client, config);
463
501
  if (!current.folder) throw setupError("SETUP_REQUIRED", "Provision the Folder and Bases with provisionDeclaredResources before publishing the AirApp");
502
+ const resolvedFiles = await resolveAirAppFileAssets(client, files);
464
503
  if (!current.airApp) {
465
504
  const pendingChangeRequestId = await findPendingAirAppCreate(client, airApp.slug);
466
505
  if (pendingChangeRequestId) return {
@@ -474,7 +513,7 @@ async function publishAirApp(client, config, files) {
474
513
  slug: airApp.slug,
475
514
  name: airApp.name,
476
515
  description: airApp.description ?? "",
477
- files,
516
+ files: resolvedFiles,
478
517
  mergeMode: "replace"
479
518
  });
480
519
  return result.materialized ? {
@@ -487,7 +526,7 @@ async function publishAirApp(client, config, files) {
487
526
  changeRequestId: result.id
488
527
  };
489
528
  }
490
- const operations = buildAirAppFileOperations(files, (await client.fileTrees.listFiles({
529
+ const operations = buildAirAppFileOperations(resolvedFiles, (await client.fileTrees.listFiles({
491
530
  nodeId: current.airApp.nodeId,
492
531
  type: "airapp"
493
532
  })).map((file) => file.path));
@@ -505,4 +544,4 @@ async function publishAirApp(client, config, files) {
505
544
  };
506
545
  }
507
546
  //#endregion
508
- export { AirAppSetupError, buildAirAppFileOperations, buildProvisionOperations, inspectProvisionedResources, isNotFound, provisionDeclaredResources, publishAirApp, resolveProvisionedFolder };
547
+ export { AirAppSetupError, buildAirAppFileOperations, buildProvisionOperations, inspectProvisionedResources, isNotFound, provisionDeclaredResources, publishAirApp, resolveAirAppFileAssets, resolveProvisionedFolder };