busa-sdk 0.10.3 → 0.11.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +18740 -18489
  2. package/dist/index.js +230 -112
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -561,7 +561,33 @@ var listNodesInputSchema = z.object({
561
561
  * soft-archived nodes for the Trash view — no `parentId`/`depth` walk, since
562
562
  * archived nodes are shown as a list, not a tree.
563
563
  */
564
- status: z.enum(["active", "archived"]).optional().default("active")
564
+ status: z.enum(["active", "archived"]).optional().default("active"),
565
+ /**
566
+ * Narrow to specific node types and return a FLAT list of lightweight node
567
+ * summaries (`children: []`) instead of walking the tree. This is what
568
+ * replaced the four retired narrow listings (`GET /docs`, `/files`,
569
+ * `/folders`, `/file-trees`); file-trees are selected with their real
570
+ * discriminators `skill` / `drive` / `airapp`, since there is no synthetic
571
+ * "file-tree" node type.
572
+ *
573
+ * Omitting `types` leaves every existing caller on exactly today's
574
+ * behaviour (full tree, or a `parentId`/`depth`-bounded walk, or the
575
+ * archived flat list) — the two modes never interfere.
576
+ *
577
+ * NOTE — no `projection` parameter, deliberately. The consolidation roadmap
578
+ * sketched `?projection=summary`, but it also rules out adding
579
+ * `projection=detail` in this batch (the retired detail lists were the
580
+ * N+1 payloads this change exists to remove). That would leave a parameter
581
+ * with exactly one legal value, which is noise in OpenAPI/MCP/CLI rather
582
+ * than a decision a caller gets to make. Detail is `GET /nodes/{nodeId}`.
583
+ *
584
+ * A GET query param that occurs exactly once (`?types=doc`) arrives as a
585
+ * bare string, not a 1-element array — only a REPEATED occurrence
586
+ * (`?types=doc&types=file`) becomes an array. Accept both and normalize.
587
+ */
588
+ types: z.union([z.array(z.enum(NODE_TYPES)), z.enum(NODE_TYPES)]).transform((value) => Array.isArray(value) ? value : [value]).optional().describe(
589
+ "Return a flat list of lightweight summaries for these node types instead of the tree. Read one node's full detail with GET /nodes/{nodeId}."
590
+ )
565
591
  }).optional();
566
592
  var isDescendantInputSchema = z.object({
567
593
  nodeId: z.string(),
@@ -729,11 +755,19 @@ var liveEventSchema = z.object({
729
755
  // via the audit funnel, but nothing needs reviewing). Consumed by
730
756
  // `use-live-sync.ts` to pop a desktop Notification, and by
731
757
  // busabase-cloud's host hook to persist an inbox notification row.
732
- "change_request.pending_review"
758
+ "change_request.pending_review",
759
+ // A node's metadata was written directly, outside the change-request flow
760
+ // (`PATCH /api/v1/nodes/{nodeId}/metadata` — agents, the SDK, an MCP tool,
761
+ // and every rich-node editor's own Save). Carries the touched node in
762
+ // `nodeIds` so open dashboards refetch the node tree instead of showing a
763
+ // stale whiteboard/workflow/HTML document until the next reload.
764
+ "node.metadata_updated"
733
765
  ]),
734
766
  spaceId: z.string(),
735
767
  actorId: z.string(),
736
- changeRequestId: z.string(),
768
+ // Null for events that aren't about a change request at all
769
+ // (`node.metadata_updated`), which is every direct, auto-audited write.
770
+ changeRequestId: z.string().nullable(),
737
771
  baseId: z.string().nullable(),
738
772
  nodeIds: z.array(z.string()),
739
773
  recordIds: z.array(z.string()),
@@ -1082,13 +1116,6 @@ var fileTreeRefSchema = z.object({
1082
1116
  type: fileTreeNodeTypeSchema.optional()
1083
1117
  });
1084
1118
  var fileTreeContract = {
1085
- list: oc.route({
1086
- method: "GET",
1087
- path: "/file-trees",
1088
- tags: ["File Trees"],
1089
- summary: "List file-tree nodes",
1090
- successDescription: "Skill, Drive, and AirApp nodes with their Asset-backed file trees. Pass `type` to narrow to one kind."
1091
- }).input(z.object({ type: fileTreeNodeTypeSchema.optional() })).output(z.array(fileTreeNodeSchema)),
1092
1119
  create: oc.route({
1093
1120
  method: "POST",
1094
1121
  path: "/file-trees",
@@ -1101,13 +1128,6 @@ var fileTreeContract = {
1101
1128
  changeRequestSchema.extend({ materialized: z.literal(false) })
1102
1129
  ])
1103
1130
  ),
1104
- get: oc.route({
1105
- method: "GET",
1106
- path: "/file-trees/{nodeId}",
1107
- tags: ["File Trees"],
1108
- summary: "Get file-tree node",
1109
- successDescription: "File-tree node detail and its file list."
1110
- }).input(fileTreeRefSchema).output(fileTreeNodeSchema),
1111
1131
  listFiles: oc.route({
1112
1132
  method: "GET",
1113
1133
  path: "/file-trees/{nodeId}/files",
@@ -1543,6 +1563,9 @@ var viewSchema = z.object({
1543
1563
  createdAt: z.string(),
1544
1564
  updatedAt: z.string()
1545
1565
  });
1566
+ var autoMergeSchema = z.boolean().optional().describe(
1567
+ "Whether to approve and merge this view change immediately. Omitted defaults to merging immediately if the actor has write access on the Base's node, otherwise falling back to a pending Change Request; pass explicit false to force review even with write access."
1568
+ );
1546
1569
  var createViewInputSchema = z.object({
1547
1570
  config: viewConfigSchema.optional().default({ filters: [], sorts: [] }),
1548
1571
  description: z.string().optional().default(""),
@@ -1550,7 +1573,8 @@ var createViewInputSchema = z.object({
1550
1573
  name: z.string().min(1),
1551
1574
  type: viewTypeSchema.optional().default("table"),
1552
1575
  slug: z.string().min(1).regex(/^[a-z0-9-]+$/).optional(),
1553
- submittedBy: z.string().optional().default("local-producer")
1576
+ submittedBy: z.string().optional().default("local-producer"),
1577
+ autoMerge: autoMergeSchema
1554
1578
  });
1555
1579
  var updateViewInputSchema = z.object({
1556
1580
  config: viewConfigSchema.optional(),
@@ -1558,15 +1582,18 @@ var updateViewInputSchema = z.object({
1558
1582
  message: z.string().optional().default("Update view"),
1559
1583
  name: z.string().min(1).optional(),
1560
1584
  type: viewTypeSchema.optional(),
1561
- submittedBy: z.string().optional().default("local-producer")
1585
+ submittedBy: z.string().optional().default("local-producer"),
1586
+ autoMerge: autoMergeSchema
1562
1587
  });
1563
1588
  var deleteViewInputSchema = z.object({
1564
1589
  message: z.string().optional().default("Delete view"),
1565
- submittedBy: z.string().optional().default("local-producer")
1590
+ submittedBy: z.string().optional().default("local-producer"),
1591
+ autoMerge: autoMergeSchema
1566
1592
  });
1567
1593
  var restoreViewInputSchema = z.object({
1568
1594
  message: z.string().optional().default("Restore view"),
1569
- submittedBy: z.string().optional().default("local-producer")
1595
+ submittedBy: z.string().optional().default("local-producer"),
1596
+ autoMerge: autoMergeSchema
1570
1597
  });
1571
1598
  var viewChangeRequestInputSchema = z.discriminatedUnion("operation", [
1572
1599
  createViewInputSchema.extend({
@@ -1903,8 +1930,13 @@ var viewContract = {
1903
1930
  path: "/views/change-requests",
1904
1931
  tags: ["Views", "Change Requests"],
1905
1932
  summary: "Create view change request",
1906
- successDescription: "Created change request proposing a view change. `operation` selects what to propose: `create` (addressed by `baseId`), or `update` / `delete` / `restore` (addressed by `viewId`)."
1907
- }).input(viewChangeRequestInputSchema).output(changeRequestSchema)
1933
+ successDescription: "Proposes a view change. `operation` selects what to propose: `create` (addressed by `baseId`), or `update` / `delete` / `restore` (addressed by `viewId`). Review-first when the actor lacks write access or passes `autoMerge: false` \u2014 a pending ChangeRequest (`materialized: false`). Otherwise the change is approved and merged in the same call and the materialized View comes back instead (`materialized: true`)."
1934
+ }).input(viewChangeRequestInputSchema).output(
1935
+ z.union([
1936
+ viewSchema.extend({ materialized: z.literal(true) }),
1937
+ changeRequestSchema.extend({ materialized: z.literal(false) })
1938
+ ])
1939
+ )
1908
1940
  };
1909
1941
  var ReadDocLinesInputSchema = z.object({
1910
1942
  nodeId: z.string(),
@@ -1943,13 +1975,6 @@ var createDocChangeRequestInputSchema = z.object({
1943
1975
  submittedBy: z.string().optional().default("local-producer")
1944
1976
  });
1945
1977
  var docContract = {
1946
- list: oc.route({
1947
- method: "GET",
1948
- path: "/docs",
1949
- tags: ["Docs"],
1950
- summary: "List Doc nodes",
1951
- successDescription: "Doc nodes with their storage-backed bodies."
1952
- }).output(z.array(docSchema)),
1953
1978
  create: oc.route({
1954
1979
  method: "POST",
1955
1980
  path: "/docs",
@@ -1962,19 +1987,12 @@ var docContract = {
1962
1987
  changeRequestSchema.extend({ materialized: z.literal(false) })
1963
1988
  ])
1964
1989
  ),
1965
- get: oc.route({
1966
- method: "GET",
1967
- path: "/docs/{nodeId}",
1968
- tags: ["Docs"],
1969
- summary: "Get Doc node",
1970
- successDescription: "Doc node detail and body."
1971
- }).input(z.object({ nodeId: z.string() })).output(docSchema),
1972
1990
  readLines: oc.route({
1973
1991
  method: "GET",
1974
1992
  path: "/docs/{nodeId}/lines",
1975
1993
  tags: ["Docs"],
1976
1994
  summary: "Read an exact line range from a Doc body",
1977
- successDescription: "Lines [startLine, endLine] (range capped at 2000 lines / ~2MB response) sliced from the Doc's full body \u2014 Docs are KB-scale, so the whole body is read in memory; no byte-range/checkpoint machinery like assets.readTextLines uses for potentially multi-GB files. The Doc-domain follow-up to a Unified Grep match with `source: \"docs\"`, so an agent can read just the lines around a match instead of `get`'s entire body."
1995
+ successDescription: "Lines [startLine, endLine] (range capped at 2000 lines / ~2MB response) sliced from the Doc's full body \u2014 Docs are KB-scale, so the whole body is read in memory; no byte-range/checkpoint machinery like assets.readTextLines uses for potentially multi-GB files. The Doc-domain follow-up to a Unified Grep match with `source: \"docs\"`, so an agent can read just the lines around a match instead of `nodes.get`'s entire body."
1978
1996
  }).input(ReadDocLinesInputSchema).output(ReadLinesVOSchema),
1979
1997
  updateBody: oc.route({
1980
1998
  method: "PUT",
@@ -2140,13 +2158,6 @@ var createFileNodeInputSchema = z.object({
2140
2158
  autoMerge: z.boolean().optional()
2141
2159
  });
2142
2160
  var fileContract = {
2143
- list: oc.route({
2144
- method: "GET",
2145
- path: "/files",
2146
- tags: ["Files"],
2147
- summary: "List File nodes",
2148
- successDescription: "Workspace File nodes with their backing Asset metadata."
2149
- }).output(z.array(FileNodeVOSchema)),
2150
2161
  create: oc.route({
2151
2162
  method: "POST",
2152
2163
  path: "/files",
@@ -2158,34 +2169,7 @@ var fileContract = {
2158
2169
  FileNodeVOSchema.extend({ materialized: z.literal(true) }),
2159
2170
  changeRequestSchema.extend({ materialized: z.literal(false) })
2160
2171
  ])
2161
- ),
2162
- get: oc.route({
2163
- method: "GET",
2164
- path: "/files/{nodeId}",
2165
- tags: ["Files"],
2166
- summary: "Get File node",
2167
- successDescription: "File node detail and backing Asset metadata."
2168
- }).input(z.object({ nodeId: z.string() })).output(FileNodeVOSchema)
2169
- };
2170
- var folderSchema = z.object({
2171
- node: nodeSchema,
2172
- children: z.array(nodeSchema)
2173
- });
2174
- var folderContract = {
2175
- list: oc.route({
2176
- method: "GET",
2177
- path: "/folders",
2178
- tags: ["Folders"],
2179
- summary: "List Folder nodes",
2180
- successDescription: "Folder nodes with their direct children."
2181
- }).output(z.array(folderSchema)),
2182
- get: oc.route({
2183
- method: "GET",
2184
- path: "/folders/{nodeId}",
2185
- tags: ["Folders"],
2186
- summary: "Get Folder node",
2187
- successDescription: "Folder node and its direct children."
2188
- }).input(z.object({ nodeId: z.string() })).output(folderSchema)
2172
+ )
2189
2173
  };
2190
2174
  var FormFieldBindingSchema = z.object({
2191
2175
  inputName: z.string().min(1),
@@ -2818,16 +2802,86 @@ var UnifiedGrepResultVOSchema = z.object({
2818
2802
  /** True when any source truncated, or any source has `notReached > 0`. */
2819
2803
  truncated: z.boolean()
2820
2804
  });
2805
+ var folderSchema = z.object({
2806
+ node: nodeSchema,
2807
+ children: z.array(nodeSchema)
2808
+ });
2809
+
2810
+ // ../../packages/busabase-contract/src/contract/node-detail-schemas.ts
2811
+ var genericNodeDetailSchema = (type) => z.object({
2812
+ type: z.literal(type),
2813
+ node: nodeSchema
2814
+ });
2815
+ var NODE_DETAIL_VARIANTS = {
2816
+ folder: folderSchema.extend({ type: z.literal("folder") }),
2817
+ doc: docSchema.extend({ type: z.literal("doc") }),
2818
+ file: FileNodeVOSchema.extend({ type: z.literal("file") }),
2819
+ // Skills, Drives, and AirApps are one server-side shape (`fileTreeNodeSchema`)
2820
+ // but three real node types — there is no synthetic "file-tree" node type, so
2821
+ // each gets its own discriminated variant rather than a shared alias.
2822
+ skill: fileTreeNodeSchema.extend({ type: z.literal("skill") }),
2823
+ drive: fileTreeNodeSchema.extend({ type: z.literal("drive") }),
2824
+ airapp: fileTreeNodeSchema.extend({ type: z.literal("airapp") }),
2825
+ base: genericNodeDetailSchema("base"),
2826
+ form: genericNodeDetailSchema("form"),
2827
+ whiteboard: genericNodeDetailSchema("whiteboard"),
2828
+ workflow: genericNodeDetailSchema("workflow"),
2829
+ html: genericNodeDetailSchema("html")
2830
+ };
2831
+ var NodeDetailVOSchema = z.discriminatedUnion("type", [
2832
+ NODE_DETAIL_VARIANTS.folder,
2833
+ NODE_DETAIL_VARIANTS.doc,
2834
+ NODE_DETAIL_VARIANTS.file,
2835
+ NODE_DETAIL_VARIANTS.skill,
2836
+ NODE_DETAIL_VARIANTS.drive,
2837
+ NODE_DETAIL_VARIANTS.airapp,
2838
+ NODE_DETAIL_VARIANTS.base,
2839
+ NODE_DETAIL_VARIANTS.form,
2840
+ NODE_DETAIL_VARIANTS.whiteboard,
2841
+ NODE_DETAIL_VARIANTS.workflow,
2842
+ NODE_DETAIL_VARIANTS.html
2843
+ ]);
2844
+ var getNodeInputSchema = z.object({
2845
+ nodeId: z.string().describe("Node id, or a slug that is unique within its type."),
2846
+ type: z.enum(NODE_TYPES).optional().describe(
2847
+ "Optional disambiguation hint, only needed when `nodeId` is a slug that exists under more than one node type."
2848
+ )
2849
+ });
2821
2850
 
2822
2851
  // ../../packages/busabase-contract/src/contract/busabase.ts
2823
- var changeRequestBatchResultSchema = z.object({
2852
+ var changeRequestBatchFailureSchema = z.object({
2853
+ changeRequestId: z.string(),
2854
+ ok: z.literal(false),
2855
+ error: z.string(),
2856
+ code: z.string().optional(),
2857
+ data: z.unknown().optional()
2858
+ });
2859
+ var changeRequestReviewBatchResultSchema = z.object({
2824
2860
  results: z.array(
2825
- z.object({
2826
- changeRequestId: z.string(),
2827
- ok: z.boolean(),
2828
- status: z.string().optional(),
2829
- error: z.string().optional()
2830
- })
2861
+ z.discriminatedUnion("ok", [
2862
+ z.object({
2863
+ changeRequestId: z.string(),
2864
+ ok: z.literal(true),
2865
+ status: z.string(),
2866
+ changeRequest: changeRequestSchema
2867
+ }),
2868
+ changeRequestBatchFailureSchema
2869
+ ])
2870
+ )
2871
+ });
2872
+ var changeRequestMergeBatchResultSchema = z.object({
2873
+ results: z.array(
2874
+ z.discriminatedUnion("ok", [
2875
+ z.object({
2876
+ changeRequestId: z.string(),
2877
+ ok: z.literal(true),
2878
+ status: z.string(),
2879
+ changeRequest: changeRequestSchema,
2880
+ record: recordSchema.nullable(),
2881
+ view: viewSchema.nullable()
2882
+ }),
2883
+ changeRequestBatchFailureSchema
2884
+ ])
2831
2885
  )
2832
2886
  });
2833
2887
  var busabaseContractRoutes = {
@@ -2862,8 +2916,8 @@ var busabaseContractRoutes = {
2862
2916
  method: "GET",
2863
2917
  path: "/nodes",
2864
2918
  tags: ["Nodes"],
2865
- summary: "List node tree",
2866
- successDescription: "Workspace node tree including folders, Bases, files, and agents. With no `parentId`/`depth`, returns the FULL tree (legacy behavior, still what every non-sidebar caller gets). Passing `parentId` and/or `depth` switches to a depth-bounded fetch: `parentId` omitted/null starts from the space root and returns it wrapped exactly like the legacy call (just depth-limited); an explicit `parentId` returns that node's children directly, ready to merge into its `NodeVO.children` for a sidebar's lazy per-folder expand. See `NodeVO.hasChildren` for how a depth boundary is surfaced."
2919
+ summary: "List nodes (workspace tree, or a flat summary list by type)",
2920
+ successDescription: "Workspace node tree including folders, Bases, files, and agents. With no `parentId`/`depth`, returns the FULL tree (legacy behavior, still what every non-sidebar caller gets). Passing `parentId` and/or `depth` switches to a depth-bounded fetch: `parentId` omitted/null starts from the space root and returns it wrapped exactly like the legacy call (just depth-limited); an explicit `parentId` returns that node's children directly, ready to merge into its `NodeVO.children` for a sidebar's lazy per-folder expand. See `NodeVO.hasChildren` for how a depth boundary is surfaced. Passing `types` instead returns a FLAT, ACL-filtered list of lightweight summaries (`children: []`) for just those node types \u2014 this is what replaced `GET /docs`, `/files`, `/folders`, and `/file-trees`, and it deliberately hydrates nothing heavy (no Doc bodies, backing Assets, folder children, or file inventories). Open one item with `GET /nodes/{nodeId}`."
2867
2921
  }).input(listNodesInputSchema).output(z.array(nodeSchema)),
2868
2922
  searchByName: oc.route({
2869
2923
  method: "GET",
@@ -2933,6 +2987,21 @@ var busabaseContractRoutes = {
2933
2987
  summary: "List the current actor's favorited nodes",
2934
2988
  successDescription: "The acting user's favorited nodes, newest-favorited first, filtered through the same archived/deleted/visibility rules as the main tree \u2014 a favorited node that's later archived, purged, or (cloud) hidden from this actor silently drops out rather than erroring."
2935
2989
  }).output(z.array(nodeSchema)),
2990
+ // Registered LAST among the `/nodes/...` GETs on purpose. `GET /nodes/search`
2991
+ // and `GET /nodes/favorites` are literal paths that now share a prefix with
2992
+ // this template. The oRPC OpenAPI matcher is a rou3 radix trie, which
2993
+ // prefers a static segment over a param segment independently of insertion
2994
+ // order — but keeping the literals declared first means the source order
2995
+ // matches the resolution order, so nobody has to know that to read this
2996
+ // file. `tests/openapi-node-routes.test.ts` proves the literals still win
2997
+ // against a real handler rather than resolving as `nodeId: "search"`.
2998
+ get: oc.route({
2999
+ method: "GET",
3000
+ path: "/nodes/{nodeId}",
3001
+ tags: ["Nodes"],
3002
+ summary: "Get one node's typed detail",
3003
+ successDescription: "The node's full detail, discriminated by its `type`. One entry point for every node type, so a caller holding an id never has to discover the type first: `folder` carries its direct `children`, `doc` its storage-backed `body`, `file` its backing `asset`, and `skill`/`drive`/`airapp` their Asset-backed `files`. Types with no richer detail yet (`base`, `form`, `whiteboard`, `workflow`, `html`) return just `node`. `nodeId` accepts an id or a slug; pass `type` when a slug exists under more than one type. Archived nodes are not returned (404), matching the typed gets this replaced."
3004
+ }).input(getNodeInputSchema).output(NodeDetailVOSchema),
2936
3005
  principals: {
2937
3006
  list: oc.route({
2938
3007
  method: "GET",
@@ -3066,7 +3135,9 @@ var busabaseContractRoutes = {
3066
3135
  airapps: airappRuntimeContract,
3067
3136
  files: fileContract,
3068
3137
  docs: docContract,
3069
- folders: folderContract,
3138
+ // No `folders` key: the Folder domain's only two operations were `GET /folders`
3139
+ // and `GET /folders/{nodeId}`, both now served by the unified Node surface
3140
+ // (`nodes.list({ types: ["folder"] })` / `nodes.get`).
3070
3141
  forms: formContract,
3071
3142
  assets: assetsContract,
3072
3143
  vault: vaultContract,
@@ -3098,23 +3169,16 @@ var busabaseContractRoutes = {
3098
3169
  successDescription: "Change Request detail."
3099
3170
  }).input(z.object({ changeRequestId: z.string() })).output(changeRequestSchema),
3100
3171
  review: oc.route({
3101
- method: "POST",
3102
- path: "/change-requests/{changeRequestId}/reviews",
3103
- tags: ["Change Requests"],
3104
- summary: "Review change request",
3105
- successDescription: "Reviewed change request."
3106
- }).input(reviewChangeRequestInputSchema.extend({ changeRequestId: z.string() })).output(changeRequestSchema),
3107
- reviewMany: oc.route({
3108
3172
  method: "POST",
3109
3173
  path: "/change-requests/reviews",
3110
3174
  tags: ["Change Requests"],
3111
- summary: "Review many change requests",
3175
+ summary: "Review change requests",
3112
3176
  successDescription: "Per-change-request review results (failures isolated \u2014 one bad id does not abort the rest)."
3113
3177
  }).input(
3114
3178
  reviewChangeRequestInputSchema.extend({
3115
3179
  changeRequestIds: z.array(z.string()).min(1).max(100)
3116
3180
  })
3117
- ).output(changeRequestBatchResultSchema),
3181
+ ).output(changeRequestReviewBatchResultSchema),
3118
3182
  close: oc.route({
3119
3183
  method: "POST",
3120
3184
  path: "/change-requests/{changeRequestId}/close",
@@ -3123,25 +3187,12 @@ var busabaseContractRoutes = {
3123
3187
  successDescription: "Closed change request (terminal \u2014 distinct from request changes)."
3124
3188
  }).input(z.object({ changeRequestId: z.string(), reason: z.string().optional() })).output(changeRequestSchema),
3125
3189
  merge: oc.route({
3126
- method: "POST",
3127
- path: "/change-requests/{changeRequestId}/merge",
3128
- tags: ["Change Requests"],
3129
- summary: "Merge change request into Base",
3130
- successDescription: "Merged change request and canonical record."
3131
- }).input(z.object({ changeRequestId: z.string() })).output(
3132
- z.object({
3133
- changeRequest: changeRequestSchema,
3134
- record: recordSchema.nullable(),
3135
- view: viewSchema.nullable()
3136
- })
3137
- ),
3138
- mergeMany: oc.route({
3139
3190
  method: "POST",
3140
3191
  path: "/change-requests/merge",
3141
3192
  tags: ["Change Requests"],
3142
- summary: "Merge many change requests",
3193
+ summary: "Merge change requests",
3143
3194
  successDescription: "Per-change-request merge results (each merged in its own transaction; failures isolated)."
3144
- }).input(z.object({ changeRequestIds: z.array(z.string()).min(1).max(100) })).output(changeRequestBatchResultSchema)
3195
+ }).input(z.object({ changeRequestIds: z.array(z.string()).min(1).max(100) })).output(changeRequestMergeBatchResultSchema)
3145
3196
  },
3146
3197
  operations: {
3147
3198
  revise: oc.route({
@@ -3482,6 +3533,10 @@ var getRecordByField = async (client, input) => {
3482
3533
  };
3483
3534
 
3484
3535
  // src/index.ts
3536
+ var batchItemError = (result) => Object.assign(new Error(result?.error ?? "Change request action returned no result"), {
3537
+ ...result?.code ? { code: result.code } : {},
3538
+ ...result?.data === void 0 ? {} : { data: result.data }
3539
+ });
3485
3540
  var Busabase = class {
3486
3541
  /** The underlying fully-typed oRPC client. Use it for anything not surfaced here. */
3487
3542
  client;
@@ -3509,11 +3564,55 @@ var Busabase = class {
3509
3564
  return this.client.views;
3510
3565
  }
3511
3566
  get changeRequests() {
3512
- return this.client.changeRequests;
3567
+ const review = async (input) => {
3568
+ if ("changeRequestIds" in input) return this.client.changeRequests.review(input);
3569
+ const { changeRequestId, ...reviewInput } = input;
3570
+ const { results } = await this.client.changeRequests.review({
3571
+ ...reviewInput,
3572
+ changeRequestIds: [changeRequestId]
3573
+ });
3574
+ const result = results[0];
3575
+ if (!result?.ok) throw batchItemError(result);
3576
+ return result.changeRequest;
3577
+ };
3578
+ const merge = async (input) => {
3579
+ if ("changeRequestIds" in input) return this.client.changeRequests.merge(input);
3580
+ const { results } = await this.client.changeRequests.merge({
3581
+ changeRequestIds: [input.changeRequestId]
3582
+ });
3583
+ const result = results[0];
3584
+ if (!result?.ok) throw batchItemError(result);
3585
+ return {
3586
+ changeRequest: result.changeRequest,
3587
+ record: result.record,
3588
+ view: result.view
3589
+ };
3590
+ };
3591
+ return new Proxy(this.client.changeRequests, {
3592
+ get(target, property, receiver) {
3593
+ if (property === "review") return review;
3594
+ if (property === "merge") return merge;
3595
+ return Reflect.get(target, property, receiver);
3596
+ }
3597
+ });
3513
3598
  }
3514
3599
  get operations() {
3515
3600
  return this.client.operations;
3516
3601
  }
3602
+ /**
3603
+ * The workspace node surface, and the single entry point for reading ONE node
3604
+ * of any type: `bb.nodes.get({ nodeId })` returns a `NodeDetailVO`
3605
+ * discriminated by `type` (`folder` carries `children`, `doc` a `body`, `file`
3606
+ * its `asset`, `skill`/`drive`/`airapp` their `files`). It replaced the four
3607
+ * typed gets (`docs`/`files`/`folders`/`fileTrees`), so a caller holding an id
3608
+ * no longer has to know the node's type before it can read it.
3609
+ *
3610
+ * `bb.nodes.list({ types })` is the matching list: a flat array of lightweight
3611
+ * summaries for just those types. Without `types` it still returns the full
3612
+ * workspace tree.
3613
+ *
3614
+ * There is no `bb.folders` any more — folders are `type: "folder"` here.
3615
+ */
3517
3616
  get nodes() {
3518
3617
  return this.client.nodes;
3519
3618
  }
@@ -3535,19 +3634,38 @@ var Busabase = class {
3535
3634
  }
3536
3635
  });
3537
3636
  }
3538
- /** Skills, Drives, and AirApps — one surface, discriminated by `type`. */
3637
+ /**
3638
+ * Skills, Drives, and AirApps — one surface, discriminated by `type`.
3639
+ *
3640
+ * Creation and per-file reads/writes live here. Listing them and reading one
3641
+ * node's detail moved to the unified Node surface:
3642
+ * `bb.nodes.list({ types: ["skill", "drive", "airapp"] })` and
3643
+ * `bb.nodes.get({ nodeId, type })`.
3644
+ */
3539
3645
  get fileTrees() {
3540
3646
  return this.client.fileTrees;
3541
3647
  }
3648
+ /**
3649
+ * File nodes. `create` only — list with `bb.nodes.list({ types: ["file"] })`
3650
+ * and read one (backing Asset included) with `bb.nodes.get({ nodeId })`.
3651
+ */
3542
3652
  get files() {
3543
3653
  return this.client.files;
3544
3654
  }
3655
+ /**
3656
+ * Docs. Create / read a line range / update the body / open a Change Request.
3657
+ * List with `bb.nodes.list({ types: ["doc"] })` and read one (body included)
3658
+ * with `bb.nodes.get({ nodeId })`.
3659
+ *
3660
+ * There is deliberately no `bb.docs.list()` shim. The retired `GET /docs`
3661
+ * returned every Doc *with its body*; the one-call replacement returns
3662
+ * lightweight summaries, and the only way to keep the old shape would be a
3663
+ * detail request per Doc. An SDK convenience that quietly turns one call into
3664
+ * N is worse than a compile error that points at `bb.nodes`.
3665
+ */
3545
3666
  get docs() {
3546
3667
  return this.client.docs;
3547
3668
  }
3548
- get folders() {
3549
- return this.client.folders;
3550
- }
3551
3669
  get agentTasks() {
3552
3670
  return this.client.agentTasks;
3553
3671
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busa-sdk",
3
- "version": "0.10.3",
3
+ "version": "0.11.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). Short-name alias for busabase-sdk.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-sdk",