busabase-sdk 0.70.0 → 0.72.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-RSU_7-Nn.js";
1
+ import { t as BusabaseClient } from "./client-CZPoK0Uk.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 };
@@ -2855,6 +2855,36 @@ declare const cloudContract: {
2855
2855
  }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
2856
2856
  };
2857
2857
  share: {
2858
+ list: import("@orpc/contract").ContractProcedure<import("@orpc/contract").Schema<unknown, unknown>, z.ZodArray<z.ZodObject<{
2859
+ nodeId: z.ZodString;
2860
+ name: z.ZodString;
2861
+ slug: z.ZodString;
2862
+ type: z.ZodEnum<{
2863
+ [x: string]: string;
2864
+ }>;
2865
+ icon: z.ZodDefault<z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
2866
+ type: z.ZodLiteral<"emoji">;
2867
+ value: z.ZodString;
2868
+ }, z.core.$strip>, z.ZodObject<{
2869
+ type: z.ZodLiteral<"attachment">;
2870
+ url: z.ZodString;
2871
+ attachmentId: z.ZodString;
2872
+ originalUrl: z.ZodOptional<z.ZodString>;
2873
+ originalAttachmentId: z.ZodOptional<z.ZodString>;
2874
+ crop: z.ZodOptional<z.ZodObject<{
2875
+ x: z.ZodNumber;
2876
+ y: z.ZodNumber;
2877
+ zoom: z.ZodNumber;
2878
+ }, z.core.$strip>>;
2879
+ }, z.core.$strip>], "type">>>;
2880
+ capability: z.ZodEnum<{
2881
+ read: "read";
2882
+ submit: "submit";
2883
+ }>;
2884
+ hasPassword: z.ZodBoolean;
2885
+ expiresAt: z.ZodNullable<z.ZodString>;
2886
+ createdAt: z.ZodString;
2887
+ }, z.core.$strip>>, import("@orpc/contract").MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
2858
2888
  get: import("@orpc/contract").ContractProcedure<z.ZodObject<{
2859
2889
  nodeId: z.ZodString;
2860
2890
  }, z.core.$strip>, z.ZodNullable<z.ZodObject<{
@@ -22941,6 +22971,78 @@ declare const cloudContract: {
22941
22971
  }, z.core.$strip>;
22942
22972
  };
22943
22973
  }>>, Record<never, never>>;
22974
+ listPaged: import("@orpc/contract").ContractProcedure<z.ZodObject<{
22975
+ status: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
22976
+ active: "active";
22977
+ all: "all";
22978
+ expired: "expired";
22979
+ revoked: "revoked";
22980
+ }>>>;
22981
+ limit: z.ZodDefault<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
22982
+ cursor: z.ZodOptional<z.ZodString>;
22983
+ }, z.core.$strip>, z.ZodObject<{
22984
+ items: z.ZodArray<z.ZodObject<{
22985
+ id: z.ZodString;
22986
+ type: z.ZodEnum<{
22987
+ "change-request": "change-request";
22988
+ node: "node";
22989
+ "record-detail": "record-detail";
22990
+ }>;
22991
+ typeId: z.ZodString;
22992
+ targetName: z.ZodString;
22993
+ nodeType: z.ZodNullable<z.ZodEnum<{
22994
+ airapp: "airapp";
22995
+ base: "base";
22996
+ doc: "doc";
22997
+ drive: "drive";
22998
+ file: "file";
22999
+ folder: "folder";
23000
+ skill: "skill";
23001
+ }>>;
23002
+ createdAt: z.ZodString;
23003
+ expiresAt: z.ZodString;
23004
+ revokedAt: z.ZodNullable<z.ZodString>;
23005
+ active: z.ZodBoolean;
23006
+ framePolicy: z.ZodObject<{
23007
+ mode: z.ZodEnum<{
23008
+ anywhere: "anywhere";
23009
+ origins: "origins";
23010
+ "top-level-only": "top-level-only";
23011
+ }>;
23012
+ allowedOrigins: z.ZodArray<z.ZodString>;
23013
+ }, z.core.$strip>;
23014
+ }, z.core.$strip>>;
23015
+ nextCursor: z.ZodNullable<z.ZodString>;
23016
+ }, z.core.$strip>, import("@orpc/contract").MergedErrorMap<Record<never, never>, import("@orpc/contract").MergedErrorMap<Record<never, never>, {
23017
+ BAD_REQUEST: {
23018
+ status: number;
23019
+ message: string;
23020
+ data: z.ZodObject<{
23021
+ error: z.ZodString;
23022
+ }, z.core.$strip>;
23023
+ };
23024
+ UNAUTHORIZED: {
23025
+ status: number;
23026
+ message: string;
23027
+ data: z.ZodObject<{
23028
+ error: z.ZodString;
23029
+ }, z.core.$strip>;
23030
+ };
23031
+ FORBIDDEN: {
23032
+ status: number;
23033
+ message: string;
23034
+ data: z.ZodObject<{
23035
+ error: z.ZodString;
23036
+ }, z.core.$strip>;
23037
+ };
23038
+ NOT_FOUND: {
23039
+ status: number;
23040
+ message: string;
23041
+ data: z.ZodObject<{
23042
+ error: z.ZodString;
23043
+ }, z.core.$strip>;
23044
+ };
23045
+ }>>, Record<never, never>>;
22944
23046
  revoke: import("@orpc/contract").ContractProcedure<z.ZodObject<{
22945
23047
  id: z.ZodString;
22946
23048
  }, z.core.$strip>, z.ZodObject<{
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as AssetTextStatus, A as NodeVO, At as GalleryCardSize, B as UpdatePreviewFileCredentialDTO, Bt as ViewSortVO, C as CommentVO, Ct as ListFormsDTO, D as MentionInboxItemVO, Dt as AssetAttachmentRef, E as LookupRollup, Et as UpdateFormDTO, F as SearchResponseVO, Ft as VIEW_FIELD_MAX_WIDTH, G as VaultItemKind, Gt as cloudContract, H as VaultAccessPolicy, Ht as ViewVO, I as SearchResultKind, It as VIEW_FIELD_MIN_WIDTH, J as VaultScopeType, Jt as CREATABLE_NODE_TYPES, K as VaultItemVO, Kt as NodeIcon, L as SearchResultVO, Lt as ViewConfigVO, M as OperationVO, Mt as GanttScale, N as ReviewVO, Nt as RecordLinkVO, O as MentionInboxPageVO, Ot as BaseFieldVO, P as ReviewVerdict, Pt as RecordVO, Q as AssetDetailVO, R as SourceAttributionVO, Rt as ViewFilterOperator, S as CommentSubjectType, St as FormVO, T as FieldType, Tt as SubmitFormDTO, U as VaultEnvironment, Ut as AttachmentRef, V as UpdateVaultSettingsDTO, Vt as ViewType, W as VaultItemInput, Wt as CloudContract, X as FileNodeMetadata, Xt as NodeType, Y as VaultSettingsVO, Yt as CreatableNodeType, Z as FileNodeVO, Zt as OperationKind, _ as ChangeRequestVO, _t as FormFieldBindingVO, a as createBusabaseClient, at as FilePreviewConfigurationStatus, b as CommentMentionTargetType, bt as FormSubmitResultVO, c as AuditAction, ct as FilePreviewUnavailableReason, d as ChangeRequestBatchFailureVO, dt as FileTreeNodeVO, et as AssetUsageVO, f as ChangeRequestCountsVO, ft as FileTreeReadFileVO, g as ChangeRequestTargetType, gt as FormBoundFieldVO, h as ChangeRequestStatus, ht as CreateFormDTO, i as ResolvedConfig, it as FilePreviewConfigVO, j as OperationStatus, jt as GalleryCoverFit, k as NodeSearchResultVO, kt as BaseVO, l as AuditEventVO, lt as FilePreviewVO, m as ChangeRequestReviewBatchResultVO, mt as ActivityItemVO, n as BusabaseConfig, nt as GrepInputDTO, o as resolveConfig, ot as FilePreviewCredentialSource, p as ChangeRequestMergeBatchResultVO, pt as NodeDetailVO, q as VaultRuntimeEnv, qt as NodeIconSchema, r as DEFAULT_BASE_URL, rt as GrepResultVO, s as AgentTaskVO, st as FilePreviewProvider, t as BusabaseClient, tt as AssetVO, u as BusabaseSourceChannel, ut as FileTreeFileVO, v as CommentMentionDispatchStatus, vt as FormPageSourceVO, w as CommitVO, wt as ListFormsVO, x as CommentMentionVO, xt as FormThemeVO, y as CommentMentionInputDTO, yt as FormShareVO, z as UserRefVO, zt as ViewFilterVO } from "./client-RSU_7-Nn.js";
1
+ import { $ as AssetTextStatus, A as NodeVO, At as GalleryCardSize, B as UpdatePreviewFileCredentialDTO, Bt as ViewSortVO, C as CommentVO, Ct as ListFormsDTO, D as MentionInboxItemVO, Dt as AssetAttachmentRef, E as LookupRollup, Et as UpdateFormDTO, F as SearchResponseVO, Ft as VIEW_FIELD_MAX_WIDTH, G as VaultItemKind, Gt as cloudContract, H as VaultAccessPolicy, Ht as ViewVO, I as SearchResultKind, It as VIEW_FIELD_MIN_WIDTH, J as VaultScopeType, Jt as CREATABLE_NODE_TYPES, K as VaultItemVO, Kt as NodeIcon, L as SearchResultVO, Lt as ViewConfigVO, M as OperationVO, Mt as GanttScale, N as ReviewVO, Nt as RecordLinkVO, O as MentionInboxPageVO, Ot as BaseFieldVO, P as ReviewVerdict, Pt as RecordVO, Q as AssetDetailVO, R as SourceAttributionVO, Rt as ViewFilterOperator, S as CommentSubjectType, St as FormVO, T as FieldType, Tt as SubmitFormDTO, U as VaultEnvironment, Ut as AttachmentRef, V as UpdateVaultSettingsDTO, Vt as ViewType, W as VaultItemInput, Wt as CloudContract, X as FileNodeMetadata, Xt as NodeType, Y as VaultSettingsVO, Yt as CreatableNodeType, Z as FileNodeVO, Zt as OperationKind, _ as ChangeRequestVO, _t as FormFieldBindingVO, a as createBusabaseClient, at as FilePreviewConfigurationStatus, b as CommentMentionTargetType, bt as FormSubmitResultVO, c as AuditAction, ct as FilePreviewUnavailableReason, d as ChangeRequestBatchFailureVO, dt as FileTreeNodeVO, et as AssetUsageVO, f as ChangeRequestCountsVO, ft as FileTreeReadFileVO, g as ChangeRequestTargetType, gt as FormBoundFieldVO, h as ChangeRequestStatus, ht as CreateFormDTO, i as ResolvedConfig, it as FilePreviewConfigVO, j as OperationStatus, jt as GalleryCoverFit, k as NodeSearchResultVO, kt as BaseVO, l as AuditEventVO, lt as FilePreviewVO, m as ChangeRequestReviewBatchResultVO, mt as ActivityItemVO, n as BusabaseConfig, nt as GrepInputDTO, o as resolveConfig, ot as FilePreviewCredentialSource, p as ChangeRequestMergeBatchResultVO, pt as NodeDetailVO, q as VaultRuntimeEnv, qt as NodeIconSchema, r as DEFAULT_BASE_URL, rt as GrepResultVO, s as AgentTaskVO, st as FilePreviewProvider, t as BusabaseClient, tt as AssetVO, u as BusabaseSourceChannel, ut as FileTreeFileVO, v as CommentMentionDispatchStatus, vt as FormPageSourceVO, w as CommitVO, wt as ListFormsVO, x as CommentMentionVO, xt as FormThemeVO, y as CommentMentionInputDTO, yt as FormShareVO, z as UserRefVO, zt as ViewFilterVO } from "./client-CZPoK0Uk.js";
2
2
  import { z } from "zod";
3
3
  //#region src/url.d.ts
4
4
  /**
@@ -114,6 +114,53 @@ declare const toFilesOnlyGrepResult: (result: UnifiedGrepResultVO) => GrepResult
114
114
  /** Call unified grep as files-only while preserving the former asset helper result shape. */
115
115
  declare const grepAssets: (client: Pick<BusabaseClient, "grep">, input: GrepInputDTO) => Promise<GrepResultVO>;
116
116
  //#endregion
117
+ //#region src/asset-upload.d.ts
118
+ /** An uploaded file, as the rest of the SDK needs to refer to it. */
119
+ interface BusabaseUploadedAsset {
120
+ /** What a file-tree operation references. Absent on hosts with no Asset library. */
121
+ assetId?: string;
122
+ /** What a record's attachment cell references. */
123
+ attachmentId: string;
124
+ /** Publicly resolvable URL — safe to embed in Markdown. */
125
+ url: string;
126
+ fileName: string;
127
+ mimeType: string;
128
+ size: number;
129
+ /** `sha256:<hex>`, when this runtime could compute one. */
130
+ contentHash?: string;
131
+ }
132
+ interface UploadAssetOptions {
133
+ fileName: string;
134
+ mimeType: string;
135
+ /** Optional grouping hint the server records alongside the asset. */
136
+ context?: string;
137
+ spaceId?: string;
138
+ }
139
+ /** The slice of the client an upload needs — keeps this testable with a stub. */
140
+ type AssetUploadClient = Pick<BusabaseClient, "assets">;
141
+ /**
142
+ * SHA-256 of the bytes, or `undefined` where WebCrypto is unavailable.
143
+ *
144
+ * Undefined is a real answer, not a failure: the hash only buys deduplication,
145
+ * so a runtime without WebCrypto uploads the bytes it would otherwise have
146
+ * skipped. Failing the upload over a missing optimization would be worse.
147
+ */
148
+ declare function hashBytes(bytes: Uint8Array): Promise<string | undefined>;
149
+ /**
150
+ * Upload bytes and get back every id the rest of the SDK might need.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const asset = await uploadAsset(client, png, {
155
+ * fileName: "user-journey.png",
156
+ * mimeType: "image/png",
157
+ * });
158
+ * // asset.assetId -> reference it from a Skill/Drive/AirApp file operation
159
+ * // asset.url -> embed it in Markdown
160
+ * ```
161
+ */
162
+ declare function uploadAsset(client: AssetUploadClient, bytes: Uint8Array, options: UploadAssetOptions, fetchImpl?: typeof fetch): Promise<BusabaseUploadedAsset>;
163
+ //#endregion
117
164
  //#region src/record-get.d.ts
118
165
  interface RecordByFieldInput {
119
166
  baseId: string;
@@ -403,6 +450,25 @@ declare class Busabase {
403
450
  * ```
404
451
  */
405
452
  putText(assetId: string, text: string): Promise<Awaited<ReturnType<BusabaseClient["assets"]["putText"]>>>;
453
+ /**
454
+ * Put a file into this Space's Asset library in one call, and get back every
455
+ * id the rest of the SDK asks for: `assetId` for a file-tree entry,
456
+ * `attachmentId` for a record's attachment cell, `url` to embed in Markdown.
457
+ *
458
+ * Hides the three-step flow (`assets.createUploadUrl` → PUT →
459
+ * `assets.confirm`) and its dedup short-circuit, the same way `putText`
460
+ * does for text slots.
461
+ *
462
+ * @example
463
+ * ```ts
464
+ * const shot = await bb.uploadAsset(png, {
465
+ * fileName: "user-journey.png",
466
+ * mimeType: "image/png",
467
+ * });
468
+ * body += `![user journey](${shot.url})`;
469
+ * ```
470
+ */
471
+ uploadAsset(bytes: Uint8Array, options: UploadAssetOptions): Promise<BusabaseUploadedAsset>;
406
472
  /** Service health — reaches the server without requiring auth. */
407
473
  health(): import("@orpc/client").ClientPromiseResult<{
408
474
  status: string;
@@ -427,4 +493,4 @@ declare class Busabase {
427
493
  }>>>>;
428
494
  }
429
495
  //#endregion
430
- export { type ActivityItemVO, type AgentTaskVO, type FileTreeFileVO as AirAppFileVO, type FileTreeFileVO as DriveFileVO, type FileTreeFileVO, type FileTreeFileVO as SkillFileVO, type FileTreeReadFileVO as AirAppReadFileVO, type FileTreeReadFileVO as DriveReadFileVO, type FileTreeReadFileVO, type FileTreeReadFileVO as SkillReadFileVO, type FileTreeNodeVO as AirAppVO, type FileTreeNodeVO as DriveVO, type FileTreeNodeVO, type FileTreeNodeVO as SkillVO, type AssetAttachmentRef, type AssetDetailVO, type AssetTextStatus, type AssetUsageVO, type AssetVO, type AttachmentRef, type AuditAction, type AuditEventVO, type BaseFieldVO, type BaseVO, Busabase, type BusabaseAssetsClient, BusabaseChangeRequestsClient, type BusabaseClient, type BusabaseConfig, type BusabaseRecordsClient, type BusabaseSourceChannel, CREATABLE_NODE_TYPES, type ChangeRequestBatchFailureVO, type ChangeRequestCountsVO, type ChangeRequestMergeBatchResultVO, type ChangeRequestReviewBatchResultVO, type ChangeRequestStatus, type ChangeRequestTargetType, type ChangeRequestVO, type CloudContract, type CommentMentionDispatchStatus, type CommentMentionInputDTO, type CommentMentionTargetType, type CommentMentionVO, type CommentSubjectType, type CommentVO, type CommitVO, type CreatableNodeType, type CreateFormDTO, DEFAULT_BASE_URL, type FieldType, type FileNodeMetadata, type FileNodeVO, type FilePreviewConfigVO, type FilePreviewConfigurationStatus, type FilePreviewCredentialSource, type FilePreviewProvider, type FilePreviewUnavailableReason, type FilePreviewVO, type FormBoundFieldVO, type FormFieldBindingVO, type FormPageSourceVO, type FormShareVO, type FormSubmitResultVO, type FormThemeVO, type FormVO, type GalleryCardSize, type GalleryCoverFit, type GanttScale, type ListFormsDTO, type ListFormsVO, type LookupRollup, type MentionInboxItemVO, type MentionInboxPageVO, type NodeDetailVO, type NodeIcon, type NodeIconSchema, type NodeSearchResultVO, type NodeType, type NodeVO, type NodeWebUrlInput, type OperationKind, type OperationStatus, type OperationVO, type RecordByFieldInput, type RecordLinkVO, type RecordVO, type ResolvedConfig, type ReviewVO, type ReviewVerdict, type SearchResponseVO, type SearchResultKind, type SearchResultVO, type SourceAttributionVO, type SubmitFormDTO, type UpdateFormDTO, type UpdatePreviewFileCredentialDTO, type UpdateVaultSettingsDTO, type UserRefVO, type VIEW_FIELD_MAX_WIDTH, type VIEW_FIELD_MIN_WIDTH, type VaultAccessPolicy, type VaultEnvironment, type VaultItemInput, type VaultItemKind, type VaultItemVO, type VaultRuntimeEnv, type VaultScopeType, type VaultSettingsVO, type ViewConfigVO, type ViewFilterOperator, type ViewFilterVO, type ViewSortVO, type ViewType, type ViewVO, cloudContract, createBusabaseClient, getRecordByField, grepAssets, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput };
496
+ export { type ActivityItemVO, type AgentTaskVO, type FileTreeFileVO as AirAppFileVO, type FileTreeFileVO as DriveFileVO, type FileTreeFileVO, type FileTreeFileVO as SkillFileVO, type FileTreeReadFileVO as AirAppReadFileVO, type FileTreeReadFileVO as DriveReadFileVO, type FileTreeReadFileVO, type FileTreeReadFileVO as SkillReadFileVO, type FileTreeNodeVO as AirAppVO, type FileTreeNodeVO as DriveVO, type FileTreeNodeVO, type FileTreeNodeVO as SkillVO, type AssetAttachmentRef, type AssetDetailVO, type AssetTextStatus, type AssetUsageVO, type AssetVO, type AttachmentRef, type AuditAction, type AuditEventVO, type BaseFieldVO, type BaseVO, Busabase, type BusabaseAssetsClient, BusabaseChangeRequestsClient, type BusabaseClient, type BusabaseConfig, type BusabaseRecordsClient, type BusabaseSourceChannel, type BusabaseUploadedAsset, CREATABLE_NODE_TYPES, type ChangeRequestBatchFailureVO, type ChangeRequestCountsVO, type ChangeRequestMergeBatchResultVO, type ChangeRequestReviewBatchResultVO, type ChangeRequestStatus, type ChangeRequestTargetType, type ChangeRequestVO, type CloudContract, type CommentMentionDispatchStatus, type CommentMentionInputDTO, type CommentMentionTargetType, type CommentMentionVO, type CommentSubjectType, type CommentVO, type CommitVO, type CreatableNodeType, type CreateFormDTO, DEFAULT_BASE_URL, type FieldType, type FileNodeMetadata, type FileNodeVO, type FilePreviewConfigVO, type FilePreviewConfigurationStatus, type FilePreviewCredentialSource, type FilePreviewProvider, type FilePreviewUnavailableReason, type FilePreviewVO, type FormBoundFieldVO, type FormFieldBindingVO, type FormPageSourceVO, type FormShareVO, type FormSubmitResultVO, type FormThemeVO, type FormVO, type GalleryCardSize, type GalleryCoverFit, type GanttScale, type ListFormsDTO, type ListFormsVO, type LookupRollup, type MentionInboxItemVO, type MentionInboxPageVO, type NodeDetailVO, type NodeIcon, type NodeIconSchema, type NodeSearchResultVO, type NodeType, type NodeVO, type NodeWebUrlInput, type OperationKind, type OperationStatus, type OperationVO, type RecordByFieldInput, type RecordLinkVO, type RecordVO, type ResolvedConfig, type ReviewVO, type ReviewVerdict, type SearchResponseVO, type SearchResultKind, type SearchResultVO, type SourceAttributionVO, type SubmitFormDTO, type UpdateFormDTO, type UpdatePreviewFileCredentialDTO, type UpdateVaultSettingsDTO, type UploadAssetOptions, type UserRefVO, type VIEW_FIELD_MAX_WIDTH, type VIEW_FIELD_MIN_WIDTH, type VaultAccessPolicy, type VaultEnvironment, type VaultItemInput, type VaultItemKind, type VaultItemVO, type VaultRuntimeEnv, type VaultScopeType, type VaultSettingsVO, type ViewConfigVO, type ViewFilterOperator, type ViewFilterVO, type ViewSortVO, type ViewType, type ViewVO, cloudContract, createBusabaseClient, getRecordByField, grepAssets, hashBytes, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput, uploadAsset };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as TemplateRiskLevelSchema } from "./template-D10kjpd-.js";
1
+ import { i as uploadAsset, n as TemplateRiskLevelSchema, r as hashBytes } from "./template-DRMp6tKv.js";
2
2
  import { t as normalizeBaseUrl } from "./url-B8GMXalA.js";
3
3
  import { ORPCError, createORPCClient } from "@orpc/client";
4
4
  import { OpenAPILink } from "@orpc/openapi-client/fetch";
@@ -1345,6 +1345,25 @@ const nodeShareSchema = z.object({
1345
1345
  expiresAt: z.string().nullable(),
1346
1346
  updatedAt: z.string()
1347
1347
  });
1348
+ const sharedNodeSchema = z.object({
1349
+ nodeId: z.string(),
1350
+ name: z.string(),
1351
+ slug: z.string(),
1352
+ type: z.enum(NODE_TYPES),
1353
+ icon: NodeIconSchema.nullable().default(null),
1354
+ capability: z.enum(["read", "submit"]),
1355
+ hasPassword: z.boolean(),
1356
+ expiresAt: z.string().nullable(),
1357
+ /**
1358
+ * When this share ROW was first created — NOT "public continuously since".
1359
+ * `disableNodeShare` flips `scope` to `"none"` and keeps the row, so a node
1360
+ * that was shared, revoked, and shared again still reports the original
1361
+ * date. There is no column recording when `scope` last became `"public"`,
1362
+ * so the UI says "first shared {time}" rather than claiming an exposure
1363
+ * window the data cannot support. Adding that column is the follow-up.
1364
+ */
1365
+ createdAt: z.string()
1366
+ });
1348
1367
  const listNodesInputSchema = z.object({
1349
1368
  parentId: z.string().nullable().optional().describe("Node to start from. Omit or null to start from the space root."),
1350
1369
  depth: z.coerce.number().int().min(1).max(5).optional().describe("How many levels beneath the start point to eagerly include (default 2 once either field is set). Capped at 5."),
@@ -4726,6 +4745,17 @@ const ListEmbedLinksInputSchema = z.object({
4726
4745
  type: EmbedTargetTypeSchema.optional().describe("Which kind of target the links point at. Omitting it returns links of every kind."),
4727
4746
  typeId: z.string().min(1).optional().describe("The target's id, interpreted according to `type`.")
4728
4747
  }).optional().default({});
4748
+ const EmbedLinkAuditStatusSchema = z.enum([
4749
+ "active",
4750
+ "expired",
4751
+ "revoked",
4752
+ "all"
4753
+ ]);
4754
+ const ListEmbedLinksPagedInputSchema = z.object({
4755
+ status: EmbedLinkAuditStatusSchema.optional().default("active").describe("Filter the workspace audit to active, expired, revoked, or all embed links."),
4756
+ limit: z.coerce.number().int().min(1).max(100).optional().default(50).describe("Embed links per page. Capped at 100; continue with `cursor`."),
4757
+ cursor: z.string().optional().describe("Opaque page cursor: pass back the `nextCursor` from the previous response. Do not construct or parse it.")
4758
+ });
4729
4759
  const RevokeEmbedLinkInputSchema = z.object({ id: z.string().min(1) });
4730
4760
  const EmbedLinkVOSchema = z.object({
4731
4761
  id: z.string(),
@@ -4739,6 +4769,10 @@ const EmbedLinkVOSchema = z.object({
4739
4769
  active: z.boolean(),
4740
4770
  framePolicy: EmbedFramePolicyVOSchema
4741
4771
  });
4772
+ const EmbedLinksPageVOSchema = z.object({
4773
+ items: z.array(EmbedLinkVOSchema),
4774
+ nextCursor: z.string().nullable()
4775
+ });
4742
4776
  const CreatedEmbedLinkVOSchema = EmbedLinkVOSchema.extend({
4743
4777
  url: z.string().url(),
4744
4778
  iframeUrl: z.string().url()
@@ -5312,6 +5346,13 @@ const busabaseContractRoutes = {
5312
5346
  summary: "List embed links the caller can manage",
5313
5347
  successDescription: "Embed link metadata without capability secrets."
5314
5348
  }).errors(embedLinkErrors).input(ListEmbedLinksInputSchema).output(z.array(EmbedLinkVOSchema)),
5349
+ listPaged: oc.route({
5350
+ method: "GET",
5351
+ path: "/embed-links/paged",
5352
+ tags: ["Embed Links"],
5353
+ summary: "Audit workspace embed links with status filtering and keyset pagination",
5354
+ successDescription: "A bounded page of manageable embed-link metadata without capability secrets."
5355
+ }).errors(embedLinkErrors).input(ListEmbedLinksPagedInputSchema).output(EmbedLinksPageVOSchema),
5315
5356
  revoke: oc.route({
5316
5357
  method: "DELETE",
5317
5358
  path: "/embed-links/{id}",
@@ -5492,6 +5533,13 @@ const busabaseContractRoutes = {
5492
5533
  })).output(z.object({ removed: z.boolean() }))
5493
5534
  },
5494
5535
  share: {
5536
+ list: oc.route({
5537
+ method: "GET",
5538
+ path: "/node-shares",
5539
+ tags: ["Nodes", "Sharing"],
5540
+ summary: "List every node in the space carrying its own live public share",
5541
+ successDescription: "One row per node that someone explicitly published and that is still live — `scope: \"public\"` and unexpired — joined to just enough of the node (name, slug, type, icon) to render and open it. Rows the caller cannot see are omitted (same node-visibility ACL as `nodes.list`), and the stored share password is never returned, only a `hasPassword` flag. Deliberately does NOT include nodes that are merely reachable because an ANCESTOR is shared: this is the list of grants a person made, each revocable on its own with `nodes.share.disable`, which is the same set the workbench sidebar marks with a globe (`NodeVO.shared`)."
5542
+ }).output(z.array(sharedNodeSchema)),
5495
5543
  get: oc.route({
5496
5544
  method: "GET",
5497
5545
  path: "/nodes/{nodeId}/share",
@@ -6190,6 +6238,27 @@ var Busabase = class {
6190
6238
  storageKey: upload.storageKey
6191
6239
  });
6192
6240
  }
6241
+ /**
6242
+ * Put a file into this Space's Asset library in one call, and get back every
6243
+ * id the rest of the SDK asks for: `assetId` for a file-tree entry,
6244
+ * `attachmentId` for a record's attachment cell, `url` to embed in Markdown.
6245
+ *
6246
+ * Hides the three-step flow (`assets.createUploadUrl` → PUT →
6247
+ * `assets.confirm`) and its dedup short-circuit, the same way `putText`
6248
+ * does for text slots.
6249
+ *
6250
+ * @example
6251
+ * ```ts
6252
+ * const shot = await bb.uploadAsset(png, {
6253
+ * fileName: "user-journey.png",
6254
+ * mimeType: "image/png",
6255
+ * });
6256
+ * body += `![user journey](${shot.url})`;
6257
+ * ```
6258
+ */
6259
+ uploadAsset(bytes, options) {
6260
+ return uploadAsset(this.client, bytes, options, this.config.fetch ?? fetch);
6261
+ }
6193
6262
  /** Service health — reaches the server without requiring auth. */
6194
6263
  health() {
6195
6264
  return this.client.system.health();
@@ -6200,4 +6269,4 @@ var Busabase = class {
6200
6269
  }
6201
6270
  };
6202
6271
  //#endregion
6203
- export { Busabase, CREATABLE_NODE_TYPES, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, getRecordByField, grepAssets, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput };
6272
+ export { Busabase, CREATABLE_NODE_TYPES, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, getRecordByField, grepAssets, hashBytes, nodeWebUrl, normalizeBaseUrl, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput, uploadAsset };
@@ -1,4 +1,82 @@
1
1
  import { z } from "zod";
2
+ //#region src/asset-upload.ts
3
+ /**
4
+ * SHA-256 of the bytes, or `undefined` where WebCrypto is unavailable.
5
+ *
6
+ * Undefined is a real answer, not a failure: the hash only buys deduplication,
7
+ * so a runtime without WebCrypto uploads the bytes it would otherwise have
8
+ * skipped. Failing the upload over a missing optimization would be worse.
9
+ */
10
+ async function hashBytes(bytes) {
11
+ const subtle = globalThis.crypto?.subtle;
12
+ if (!subtle) return void 0;
13
+ const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
14
+ const digest = await subtle.digest("SHA-256", buffer);
15
+ return `sha256:${Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
16
+ }
17
+ /**
18
+ * Upload bytes and get back every id the rest of the SDK might need.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const asset = await uploadAsset(client, png, {
23
+ * fileName: "user-journey.png",
24
+ * mimeType: "image/png",
25
+ * });
26
+ * // asset.assetId -> reference it from a Skill/Drive/AirApp file operation
27
+ * // asset.url -> embed it in Markdown
28
+ * ```
29
+ */
30
+ async function uploadAsset(client, bytes, options, fetchImpl = fetch) {
31
+ const { fileName, mimeType, context, spaceId } = options;
32
+ if (!bytes.byteLength) throw new Error(`uploadAsset: ${fileName} is empty — there are no bytes to upload`);
33
+ const contentHash = await hashBytes(bytes);
34
+ const upload = await client.assets.createUploadUrl({
35
+ fileName,
36
+ mimeType,
37
+ sizeBytes: bytes.byteLength,
38
+ ...context ? { context } : {},
39
+ ...spaceId ? { spaceId } : {},
40
+ ...contentHash ? { contentHash } : {}
41
+ });
42
+ if (upload.duplicate && upload.attachmentId) return {
43
+ ...upload.assetId ? { assetId: upload.assetId } : {},
44
+ attachmentId: upload.attachmentId,
45
+ url: upload.publicUrl,
46
+ fileName,
47
+ mimeType,
48
+ size: bytes.byteLength,
49
+ ...contentHash ? { contentHash } : {}
50
+ };
51
+ const response = await fetchImpl(upload.uploadUrl, {
52
+ method: "PUT",
53
+ headers: { "content-type": mimeType },
54
+ body: bytes
55
+ });
56
+ if (!response.ok) {
57
+ const detail = await response.text().catch(() => "");
58
+ throw new Error(`uploadAsset: presigned upload of ${fileName} failed (${response.status} ${response.statusText})${detail ? `: ${detail}` : ""}`);
59
+ }
60
+ const confirmed = await client.assets.confirm({
61
+ storageKey: upload.storageKey,
62
+ fileName,
63
+ mimeType,
64
+ sizeBytes: bytes.byteLength,
65
+ ...context ? { context } : {},
66
+ ...spaceId ? { spaceId } : {},
67
+ ...contentHash ? { contentHash } : {}
68
+ });
69
+ return {
70
+ ...confirmed.assetId ? { assetId: confirmed.assetId } : {},
71
+ attachmentId: confirmed.attachmentId,
72
+ url: confirmed.publicUrl,
73
+ fileName,
74
+ mimeType,
75
+ size: bytes.byteLength,
76
+ ...contentHash ? { contentHash } : {}
77
+ };
78
+ }
79
+ //#endregion
2
80
  //#region ../../packages/busabase-contract/src/domains/skill/frontmatter.ts
3
81
  /**
4
82
  * `SKILL.md` frontmatter — the generic shape, independent of Busabase.
@@ -157,4 +235,4 @@ z.object({
157
235
  ["isTemplateSkill"]: z.literal(true)
158
236
  });
159
237
  //#endregion
160
- export { TemplateRiskLevelSchema as n, APP_ROOT_RESOURCE_KEY as t };
238
+ export { uploadAsset as i, TemplateRiskLevelSchema as n, hashBytes as r, APP_ROOT_RESOURCE_KEY as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busabase-sdk",
3
- "version": "0.70.0",
3
+ "version": "0.72.0",
4
4
  "description": "Typed TypeScript/JavaScript SDK for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server` (or Busabase Cloud).",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-sdk",
@@ -67,9 +67,9 @@
67
67
  "tsx": "^4.20.5",
68
68
  "typescript": "^7.0.2",
69
69
  "vitest": "^4.1.11",
70
- "busabase-contract": "0.70.0",
71
- "openlib": "0.1.1",
72
- "open-domains": "0.0.2"
70
+ "open-domains": "0.0.2",
71
+ "busabase-contract": "0.72.0",
72
+ "openlib": "0.1.1"
73
73
  },
74
74
  "engines": {
75
75
  "node": ">=24.18.0"