busa-sdk 0.10.2 → 0.10.3

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/index.js CHANGED
@@ -1,9 +1,34 @@
1
+ import { normalizeBaseUrl } from './chunk-5NYQX65A.js';
2
+ export { normalizeBaseUrl } from './chunk-5NYQX65A.js';
1
3
  import { createORPCClient, ORPCError } from '@orpc/client';
2
4
  import { OpenAPILink } from '@orpc/openapi-client/fetch';
3
5
  import { oc, eventIterator } from '@orpc/contract';
4
6
  import { z } from 'zod';
5
7
 
6
- // src/client.ts
8
+ // src/asset-grep.ts
9
+ var toUnifiedFilesGrepInput = (input) => ({
10
+ pattern: input.pattern,
11
+ flags: input.flags,
12
+ sources: ["files"],
13
+ scope: input.scope ? { files: input.scope } : void 0,
14
+ maxMatches: input.maxMatches,
15
+ contextLines: input.contextLines
16
+ });
17
+ var toFilesOnlyGrepResult = (result) => ({
18
+ matches: result.matches.flatMap((match) => {
19
+ if (match.source !== "files") return [];
20
+ const { source: _source, ...fileMatch } = match;
21
+ return [fileMatch];
22
+ }),
23
+ filesScanned: result.coverage.files.scanned,
24
+ missing: result.coverage.files.missing,
25
+ stale: result.coverage.files.stale,
26
+ unsearchable: result.coverage.files.unsearchable,
27
+ errored: result.coverage.files.errored,
28
+ notReached: result.coverage.files.notReached,
29
+ truncated: result.truncated
30
+ });
31
+ var grepAssets = async (client, input) => toFilesOnlyGrepResult(await client.grep(toUnifiedFilesGrepInput(input)));
7
32
  var i18n = {
8
33
  locales: ["en", "zh-CN", "zh-TW", "ja", "ko", "de", "fr", "es", "pt"]};
9
34
  var LocaleSchema = z.enum(i18n.locales);
@@ -1277,7 +1302,7 @@ var GREP_DEFAULT_MAX_MATCHES = 100;
1277
1302
  var GREP_HARD_MAX_MATCHES = 1e3;
1278
1303
  var GREP_DEFAULT_CONTEXT_LINES = 0;
1279
1304
  var GREP_MAX_CONTEXT_LINES = 10;
1280
- var GrepInputSchema = z.object({
1305
+ z.object({
1281
1306
  pattern: z.string().min(1),
1282
1307
  /** JS RegExp flags, e.g. `"i"` for case-insensitive. `g`/`y` are ignored (grep always scans every match per line). */
1283
1308
  flags: z.string().optional().default(""),
@@ -1298,7 +1323,7 @@ var GrepMatchVOSchema = z.object({
1298
1323
  before: z.array(z.string()),
1299
1324
  after: z.array(z.string())
1300
1325
  });
1301
- var GrepResultVOSchema = z.object({
1326
+ z.object({
1302
1327
  matches: z.array(GrepMatchVOSchema),
1303
1328
  filesScanned: z.number().int().nonnegative(),
1304
1329
  /** Asset ids in scope with no text yet (contentKind text-or-writable-binary, no row). */
@@ -1432,13 +1457,6 @@ var assetsContract = {
1432
1457
  summary: "Request a presigned upload URL for large text",
1433
1458
  successDescription: "Presigned (or dev) upload URL for a temporary text object; PUT the bytes there, then call putText with the returned storageKey to bind, verify, and content-address it."
1434
1459
  }).input(CreateTextUploadUrlInputSchema).output(CreateTextUploadUrlVOSchema),
1435
- grep: oc.route({
1436
- method: "POST",
1437
- path: "/assets/grep",
1438
- tags: ["Assets"],
1439
- summary: "Search every text-bearing asset in scope",
1440
- successDescription: "Streaming regex/literal matches with real file + line + column numbers and context, across every asset with text \u2014 any size, no 256KB cap. Honest coverage: missing/stale/unsearchable/errored name assets that were not (fully or successfully) searched, notReached counts present assets the scan never got to, and truncated flags a capped response."
1441
- }).input(GrepInputSchema).output(GrepResultVOSchema),
1442
1460
  readTextLines: oc.route({
1443
1461
  method: "GET",
1444
1462
  path: "/assets/{assetId}/text/lines",
@@ -1616,6 +1634,19 @@ var listRecordsResponseSchema = z.object({
1616
1634
  records: z.array(recordSchema),
1617
1635
  nextCursor: z.string().nullable()
1618
1636
  });
1637
+ var listRecordsPageInputSchema = z.object({
1638
+ baseId: z.string().min(1),
1639
+ viewId: z.string().min(1).optional(),
1640
+ page: z.coerce.number().int().min(1).optional().default(1),
1641
+ pageSize: z.coerce.number().int().min(1).max(100).optional().default(50)
1642
+ });
1643
+ var listRecordsPageResponseSchema = z.object({
1644
+ records: z.array(recordSchema),
1645
+ total: z.number().int().nonnegative(),
1646
+ totalPages: z.number().int().nonnegative(),
1647
+ page: z.number().int().min(1),
1648
+ pageSize: z.number().int().min(1).max(100)
1649
+ });
1619
1650
  var countRecordsInputSchema = z.object({
1620
1651
  baseId: z.string().optional()
1621
1652
  }).optional().default({});
@@ -1662,10 +1693,16 @@ var recordFieldFilterInputSchema = z.object({
1662
1693
  limit: z.coerce.number().int().min(1).max(100).optional().default(50)
1663
1694
  });
1664
1695
  var recordFieldGetInputSchema = z.object({
1665
- baseId: z.string(),
1666
- fieldSlug: z.string().min(1),
1667
- valueText: z.string().min(1)
1696
+ baseId: z.string().describe("Field selector: Base id. Requires fieldSlug and valueText."),
1697
+ fieldSlug: z.string().min(1).describe("Field selector: exact field slug. Requires baseId and valueText."),
1698
+ valueText: z.string().min(1).describe("Field selector: exact text value. Requires baseId and fieldSlug.")
1668
1699
  });
1700
+ var recordGetInputSchema = z.union([
1701
+ z.object({
1702
+ recordId: z.string().min(1).describe("Record id selector. Use alone; do not combine with field selector fields.")
1703
+ }).strict(),
1704
+ recordFieldGetInputSchema.strict()
1705
+ ]);
1669
1706
  var restoreRecordInputSchema = z.object({
1670
1707
  message: z.string().optional(),
1671
1708
  submittedBy: z.string().optional().default("local-editor")
@@ -1801,6 +1838,13 @@ var recordContract = {
1801
1838
  summary: "List records",
1802
1839
  successDescription: "A page of canonical records plus an opaque nextCursor (null at the end). `status=archived` lists the Base's trash instead of its live rows."
1803
1840
  }).input(listRecordsInputSchema).output(listRecordsResponseSchema),
1841
+ listPage: oc.route({
1842
+ method: "GET",
1843
+ path: "/records/page",
1844
+ tags: ["Records"],
1845
+ summary: "List a numbered record page",
1846
+ successDescription: "A random-access page of active records. When viewId is supplied, the saved view is authoritatively filtered and sorted before total and page slicing are calculated."
1847
+ }).input(listRecordsPageInputSchema).output(listRecordsPageResponseSchema),
1804
1848
  count: oc.route({
1805
1849
  method: "GET",
1806
1850
  path: "/records/count",
@@ -1810,11 +1854,15 @@ var recordContract = {
1810
1854
  }).input(countRecordsInputSchema).output(countRecordsResponseSchema),
1811
1855
  get: oc.route({
1812
1856
  method: "GET",
1813
- path: "/records/{recordId}",
1857
+ path: "/records/get",
1814
1858
  tags: ["Records"],
1815
1859
  summary: "Get record",
1816
- successDescription: "Canonical record detail."
1817
- }).input(z.object({ recordId: z.string() })).output(recordSchema),
1860
+ description: "Provide exactly one selector: recordId alone, or the complete baseId + fieldSlug + valueText tuple. Other combinations return 400.",
1861
+ successDescription: "One canonical record selected by id or exact field value."
1862
+ }).errors({
1863
+ BAD_REQUEST: { status: 400, message: "Exactly one record selector is required" },
1864
+ NOT_FOUND: { status: 404, message: "Record not found" }
1865
+ }).input(recordGetInputSchema).output(recordSchema),
1818
1866
  search: oc.route({
1819
1867
  method: "GET",
1820
1868
  path: "/records/search",
@@ -1822,13 +1870,6 @@ var recordContract = {
1822
1870
  summary: "Filter records by field text",
1823
1871
  successDescription: "Canonical records matching a field text filter."
1824
1872
  }).input(recordFieldFilterInputSchema).output(z.array(recordSchema)),
1825
- getByField: oc.route({
1826
- method: "GET",
1827
- path: "/records/by-field",
1828
- tags: ["Records"],
1829
- summary: "Get record by field value",
1830
- successDescription: "Single canonical record whose field value exactly matches, or null when none does \u2014 a scoped point lookup (e.g. by a unique slug or path field), not a list."
1831
- }).input(recordFieldGetInputSchema).output(recordSchema.nullable()),
1832
1873
  changeRequest: oc.route({
1833
1874
  method: "POST",
1834
1875
  path: "/records/{recordId}/change-requests",
@@ -2697,7 +2738,7 @@ var UnifiedGrepScopeSchema = z.object({
2697
2738
  });
2698
2739
  var UnifiedGrepInputSchema = z.object({
2699
2740
  pattern: z.string().min(1),
2700
- /** JS RegExp flags, e.g. `"i"` for case-insensitive — same language as `assets.grep`. */
2741
+ /** JS RegExp flags, e.g. `"i"` for case-insensitive. */
2701
2742
  flags: z.string().optional().default(""),
2702
2743
  /** Which sources to scan. Omitted = all three (`files`, `docs`, `records`). */
2703
2744
  sources: z.array(GrepSourceSchema).optional(),
@@ -2806,11 +2847,9 @@ var busabaseContractRoutes = {
2806
2847
  summary: "Search Busabase",
2807
2848
  successDescription: "Paginated search results across records, change requests, Bases, File nodes, and Assets."
2808
2849
  }).input(searchInputSchema).output(searchResponseSchema),
2809
- // Unified Grep (P2a files+docs, P2b records) — top-level, cross-source
2810
- // superset of `assets.grep`. See apps/busabase/content/spec/unified-grep.md.
2811
- // Composes `logic/grep.ts`; `assets.grep` (files-only specialist) is
2812
- // unchanged and stays the dedicated endpoint for its fuller
2813
- // missing/stale/unsearchable reporting.
2850
+ // Unified Grep (P2a files+docs, P2b records) — the single public pattern
2851
+ // search endpoint. Files-only callers use `sources: ["files"]` and retain
2852
+ // the full missing/stale/unsearchable coverage block.
2814
2853
  grep: oc.route({
2815
2854
  method: "POST",
2816
2855
  path: "/grep",
@@ -3384,9 +3423,6 @@ var env = (key) => {
3384
3423
  const value = process.env[key];
3385
3424
  return value && value.length > 0 ? value : void 0;
3386
3425
  };
3387
- function normalizeBaseUrl(raw) {
3388
- return raw.replace(/\/+$/, "").replace(/\/api\/v1$/, "");
3389
- }
3390
3426
  function resolveConfig(config = {}) {
3391
3427
  return {
3392
3428
  baseUrl: normalizeBaseUrl(config.baseUrl ?? env("BUSABASE_BASE_URL") ?? DEFAULT_BASE_URL),
@@ -3434,6 +3470,17 @@ function createBusabaseClient(config = {}) {
3434
3470
  return createORPCClient(link);
3435
3471
  }
3436
3472
 
3473
+ // src/record-get.ts
3474
+ var isNotFound = (error) => typeof error === "object" && error !== null && ("status" in error && error.status === 404 || "code" in error && error.code === "NOT_FOUND");
3475
+ var getRecordByField = async (client, input) => {
3476
+ try {
3477
+ return await client.records.get(input);
3478
+ } catch (error) {
3479
+ if (isNotFound(error)) return null;
3480
+ throw error;
3481
+ }
3482
+ };
3483
+
3437
3484
  // src/index.ts
3438
3485
  var Busabase = class {
3439
3486
  /** The underlying fully-typed oRPC client. Use it for anything not surfaced here. */
@@ -3450,7 +3497,13 @@ var Busabase = class {
3450
3497
  return this.client.bases;
3451
3498
  }
3452
3499
  get records() {
3453
- return this.client.records;
3500
+ const getByField = (input) => getRecordByField(this.client, input);
3501
+ return new Proxy(this.client.records, {
3502
+ get(target, property, receiver) {
3503
+ if (property === "getByField") return getByField;
3504
+ return Reflect.get(target, property, receiver);
3505
+ }
3506
+ });
3454
3507
  }
3455
3508
  get views() {
3456
3509
  return this.client.views;
@@ -3474,7 +3527,13 @@ var Busabase = class {
3474
3527
  return this.client.agent;
3475
3528
  }
3476
3529
  get assets() {
3477
- return this.client.assets;
3530
+ const filesOnlyGrep = (input) => grepAssets(this.client, input);
3531
+ return new Proxy(this.client.assets, {
3532
+ get(target, property, receiver) {
3533
+ if (property === "grep") return filesOnlyGrep;
3534
+ return Reflect.get(target, property, receiver);
3535
+ }
3536
+ });
3478
3537
  }
3479
3538
  /** Skills, Drives, and AirApps — one surface, discriminated by `type`. */
3480
3539
  get fileTrees() {
@@ -3507,10 +3566,8 @@ var Busabase = class {
3507
3566
  * source (Drive/Skill files, Doc bodies, and Base records — records read
3508
3567
  * the canonical `headCommit.fields`, never the truncated search
3509
3568
  * projection), with a shared `maxMatches`/deadline budget and per-source
3510
- * honest coverage. Use this when the answer could live anywhere; use
3511
- * `client.assets.grep` directly instead when you specifically only care
3512
- * about files and want its fuller `missing`/`stale`/`unsearchable`
3513
- * file-only reporting.
3569
+ * honest coverage. `bb.assets.grep` remains available as a files-only SDK
3570
+ * convenience and delegates here with `sources: ["files"]`.
3514
3571
  */
3515
3572
  grep(input) {
3516
3573
  return this.client.grep(input);
@@ -3560,4 +3617,4 @@ var Busabase = class {
3560
3617
  }
3561
3618
  };
3562
3619
 
3563
- export { Busabase, CREATABLE_NODE_TYPES, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, normalizeBaseUrl, resolveConfig };
3620
+ export { Busabase, CREATABLE_NODE_TYPES, DEFAULT_BASE_URL, cloudContract, createBusabaseClient, getRecordByField, grepAssets, resolveConfig, toFilesOnlyGrepResult, toUnifiedFilesGrepInput };
@@ -0,0 +1,33 @@
1
+ import { BusabaseOAuthTokenSet } from './oauth.js';
2
+
3
+ interface BusabaseAirAppOAuthCredential {
4
+ version: 1;
5
+ appId: string;
6
+ baseUrl: string;
7
+ clientId: string;
8
+ accessToken: string;
9
+ refreshToken: string;
10
+ expiresAt: string;
11
+ scope: string[];
12
+ tokenType: string;
13
+ }
14
+ interface BusabaseAirAppCredentialStoreOptions {
15
+ /** Override only for tests or an explicitly isolated installation. */
16
+ rootDir?: string;
17
+ }
18
+ /** Directory containing one owner-only OAuth registration per local AirApp. */
19
+ declare const busabaseAirAppCredentialsDir: (options?: BusabaseAirAppCredentialStoreOptions) => string;
20
+ declare const busabaseAirAppCredentialPath: (appId: string, options?: BusabaseAirAppCredentialStoreOptions) => string;
21
+ declare function loadBusabaseAirAppOAuthCredential(appId: string, options?: BusabaseAirAppCredentialStoreOptions): BusabaseAirAppOAuthCredential | null;
22
+ declare function storeBusabaseAirAppOAuthCredential(input: {
23
+ appId: string;
24
+ baseUrl: string;
25
+ tokenSet: BusabaseOAuthTokenSet;
26
+ clientId?: string;
27
+ }, options?: BusabaseAirAppCredentialStoreOptions): BusabaseAirAppOAuthCredential;
28
+ /** Load a valid access token, rotating and persisting the token set when needed. */
29
+ declare function getBusabaseAirAppAccessToken(appId: string, options?: BusabaseAirAppCredentialStoreOptions, fetchImpl?: typeof fetch): Promise<BusabaseAirAppOAuthCredential | null>;
30
+ declare function clearBusabaseAirAppOAuthCredential(appId: string, options?: BusabaseAirAppCredentialStoreOptions): void;
31
+ declare function revokeBusabaseAirAppOAuthCredential(appId: string, options?: BusabaseAirAppCredentialStoreOptions, fetchImpl?: typeof fetch): Promise<void>;
32
+
33
+ export { type BusabaseAirAppCredentialStoreOptions, type BusabaseAirAppOAuthCredential, busabaseAirAppCredentialPath, busabaseAirAppCredentialsDir, clearBusabaseAirAppOAuthCredential, getBusabaseAirAppAccessToken, loadBusabaseAirAppOAuthCredential, revokeBusabaseAirAppOAuthCredential, storeBusabaseAirAppOAuthCredential };
@@ -0,0 +1,147 @@
1
+ import { BusabaseOAuthError, BUSABASE_AIRAPP_CLIENT_ID, refreshBusabaseOAuthToken, revokeBusabaseOAuthToken } from './chunk-J2DZKX7A.js';
2
+ import { normalizeBaseUrl } from './chunk-5NYQX65A.js';
3
+ import { randomUUID } from 'crypto';
4
+ import { readFileSync, mkdirSync, chmodSync, writeFileSync, renameSync, rmSync } from 'fs';
5
+ import { homedir } from 'os';
6
+ import { join, dirname } from 'path';
7
+
8
+ var STORE_VERSION = 1;
9
+ var APP_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
10
+ var REFRESH_WINDOW_MS = 6e4;
11
+ var refreshesByCredentialPath = /* @__PURE__ */ new Map();
12
+ var assertAppId = (appId) => {
13
+ if (!APP_ID_RE.test(appId)) {
14
+ throw new BusabaseOAuthError(
15
+ "invalid_airapp_id",
16
+ "AirApp id must use letters, digits, dot, dash, or underscore"
17
+ );
18
+ }
19
+ return appId;
20
+ };
21
+ var storeRoot = (options = {}) => options.rootDir ?? join(homedir(), ".busabase");
22
+ var busabaseAirAppCredentialsDir = (options = {}) => join(storeRoot(options), "airapps");
23
+ var busabaseAirAppCredentialPath = (appId, options = {}) => join(busabaseAirAppCredentialsDir(options), `${assertAppId(appId)}.json`);
24
+ var normalizeOrigin = (raw) => {
25
+ const url = new URL(normalizeBaseUrl(raw));
26
+ if (url.username || url.password || url.search || url.hash) {
27
+ throw new BusabaseOAuthError("invalid_base_url", "Busabase base URL must be an origin");
28
+ }
29
+ return url.origin;
30
+ };
31
+ var parseCredential = (raw, expectedAppId) => {
32
+ let value;
33
+ try {
34
+ value = JSON.parse(raw);
35
+ } catch {
36
+ throw new BusabaseOAuthError("invalid_local_credential", "AirApp OAuth credential is invalid");
37
+ }
38
+ const item = value;
39
+ if (item.version !== STORE_VERSION || item.appId !== expectedAppId || typeof item.baseUrl !== "string" || typeof item.clientId !== "string" || typeof item.accessToken !== "string" || typeof item.refreshToken !== "string" || typeof item.expiresAt !== "string" || !Array.isArray(item.scope) || item.scope.some((scope) => typeof scope !== "string") || typeof item.tokenType !== "string") {
40
+ throw new BusabaseOAuthError("invalid_local_credential", "AirApp OAuth credential is invalid");
41
+ }
42
+ return item;
43
+ };
44
+ function loadBusabaseAirAppOAuthCredential(appId, options = {}) {
45
+ const path = busabaseAirAppCredentialPath(appId, options);
46
+ try {
47
+ return parseCredential(readFileSync(path, "utf8"), appId);
48
+ } catch (error) {
49
+ if (error.code === "ENOENT") return null;
50
+ throw error;
51
+ }
52
+ }
53
+ function storeBusabaseAirAppOAuthCredential(input, options = {}) {
54
+ if (!input.tokenSet.refreshToken) {
55
+ throw new BusabaseOAuthError(
56
+ "missing_refresh_token",
57
+ "A refresh token is required for a persistent local AirApp login"
58
+ );
59
+ }
60
+ const credential = {
61
+ version: STORE_VERSION,
62
+ appId: assertAppId(input.appId),
63
+ baseUrl: normalizeOrigin(input.baseUrl),
64
+ clientId: input.clientId ?? BUSABASE_AIRAPP_CLIENT_ID,
65
+ accessToken: input.tokenSet.accessToken,
66
+ refreshToken: input.tokenSet.refreshToken,
67
+ expiresAt: input.tokenSet.expiresAt,
68
+ scope: input.tokenSet.scope,
69
+ tokenType: input.tokenSet.tokenType
70
+ };
71
+ const path = busabaseAirAppCredentialPath(input.appId, options);
72
+ const directory = dirname(path);
73
+ mkdirSync(directory, { recursive: true, mode: 448 });
74
+ try {
75
+ chmodSync(directory, 448);
76
+ } catch {
77
+ }
78
+ const temporaryPath = `${path}.${randomUUID()}.tmp`;
79
+ writeFileSync(temporaryPath, `${JSON.stringify(credential, null, 2)}
80
+ `, { mode: 384 });
81
+ try {
82
+ chmodSync(temporaryPath, 384);
83
+ } catch {
84
+ }
85
+ renameSync(temporaryPath, path);
86
+ return credential;
87
+ }
88
+ async function getBusabaseAirAppAccessToken(appId, options = {}, fetchImpl = fetch) {
89
+ const credential = loadBusabaseAirAppOAuthCredential(appId, options);
90
+ if (!credential) return null;
91
+ const expiresAt = Date.parse(credential.expiresAt);
92
+ if (Number.isFinite(expiresAt) && expiresAt > Date.now() + REFRESH_WINDOW_MS) return credential;
93
+ const credentialPath = busabaseAirAppCredentialPath(appId, options);
94
+ const activeRefresh = refreshesByCredentialPath.get(credentialPath);
95
+ if (activeRefresh) return activeRefresh;
96
+ const refresh = (async () => {
97
+ const tokenSet = await refreshBusabaseOAuthToken(
98
+ {
99
+ baseUrl: credential.baseUrl,
100
+ refreshToken: credential.refreshToken,
101
+ clientId: credential.clientId
102
+ },
103
+ fetchImpl
104
+ );
105
+ return storeBusabaseAirAppOAuthCredential(
106
+ {
107
+ appId,
108
+ baseUrl: credential.baseUrl,
109
+ clientId: credential.clientId,
110
+ tokenSet: {
111
+ ...tokenSet,
112
+ refreshToken: tokenSet.refreshToken ?? credential.refreshToken
113
+ }
114
+ },
115
+ options
116
+ );
117
+ })();
118
+ refreshesByCredentialPath.set(credentialPath, refresh);
119
+ try {
120
+ return await refresh;
121
+ } finally {
122
+ if (refreshesByCredentialPath.get(credentialPath) === refresh) {
123
+ refreshesByCredentialPath.delete(credentialPath);
124
+ }
125
+ }
126
+ }
127
+ function clearBusabaseAirAppOAuthCredential(appId, options = {}) {
128
+ rmSync(busabaseAirAppCredentialPath(appId, options), { force: true });
129
+ }
130
+ async function revokeBusabaseAirAppOAuthCredential(appId, options = {}, fetchImpl = fetch) {
131
+ const credential = loadBusabaseAirAppOAuthCredential(appId, options);
132
+ if (!credential) return;
133
+ try {
134
+ await revokeBusabaseOAuthToken(
135
+ {
136
+ baseUrl: credential.baseUrl,
137
+ token: credential.refreshToken,
138
+ clientId: credential.clientId
139
+ },
140
+ fetchImpl
141
+ );
142
+ } finally {
143
+ clearBusabaseAirAppOAuthCredential(appId, options);
144
+ }
145
+ }
146
+
147
+ export { busabaseAirAppCredentialPath, busabaseAirAppCredentialsDir, clearBusabaseAirAppOAuthCredential, getBusabaseAirAppAccessToken, loadBusabaseAirAppOAuthCredential, revokeBusabaseAirAppOAuthCredential, storeBusabaseAirAppOAuthCredential };
@@ -0,0 +1,55 @@
1
+ /** Public client identifier used by local Hono/AirApp development servers. */
2
+ declare const BUSABASE_AIRAPP_CLIENT_ID = "busabase-airapp";
3
+
4
+ interface BusabaseOAuthRequest {
5
+ authorizeUrl: string;
6
+ baseUrl: string;
7
+ clientId: string;
8
+ codeVerifier: string;
9
+ redirectUri: string;
10
+ resource: string;
11
+ state: string;
12
+ }
13
+ interface CreateBusabaseOAuthRequestInput {
14
+ baseUrl: string;
15
+ redirectUri: string;
16
+ clientId?: string;
17
+ state?: string;
18
+ prompt?: "login";
19
+ }
20
+ interface BusabaseOAuthTokenSet {
21
+ accessToken: string;
22
+ refreshToken?: string;
23
+ expiresIn: number;
24
+ expiresAt: string;
25
+ scope: string[];
26
+ tokenType: string;
27
+ user?: {
28
+ id: string;
29
+ name: string;
30
+ email: string;
31
+ image: string | null;
32
+ };
33
+ }
34
+ declare class BusabaseOAuthError extends Error {
35
+ readonly code: string;
36
+ readonly status?: number;
37
+ constructor(code: string, message: string, status?: number);
38
+ }
39
+ /** Build a public-client OAuth 2.1 authorization request with PKCE S256. */
40
+ declare function createBusabaseOAuthRequest(input: CreateBusabaseOAuthRequestInput): Promise<BusabaseOAuthRequest>;
41
+ /** Validate state and issuer before accepting the authorization code. */
42
+ declare function parseBusabaseOAuthCallback(callbackUrl: string, request: BusabaseOAuthRequest): string;
43
+ declare function exchangeBusabaseOAuthCode(request: BusabaseOAuthRequest, code: string, fetchImpl?: typeof fetch): Promise<BusabaseOAuthTokenSet>;
44
+ declare function refreshBusabaseOAuthToken(input: {
45
+ baseUrl: string;
46
+ refreshToken: string;
47
+ clientId?: string;
48
+ }, fetchImpl?: typeof fetch): Promise<BusabaseOAuthTokenSet>;
49
+ declare function revokeBusabaseOAuthToken(input: {
50
+ baseUrl: string;
51
+ token: string;
52
+ clientId?: string;
53
+ }, fetchImpl?: typeof fetch): Promise<void>;
54
+
55
+ export { BUSABASE_AIRAPP_CLIENT_ID, BusabaseOAuthError, type BusabaseOAuthRequest, type BusabaseOAuthTokenSet, type CreateBusabaseOAuthRequestInput, createBusabaseOAuthRequest, exchangeBusabaseOAuthCode, parseBusabaseOAuthCallback, refreshBusabaseOAuthToken, revokeBusabaseOAuthToken };
package/dist/oauth.js ADDED
@@ -0,0 +1,2 @@
1
+ export { BUSABASE_AIRAPP_CLIENT_ID, BusabaseOAuthError, createBusabaseOAuthRequest, exchangeBusabaseOAuthCode, parseBusabaseOAuthCallback, refreshBusabaseOAuthToken, revokeBusabaseOAuthToken } from './chunk-J2DZKX7A.js';
2
+ import './chunk-5NYQX65A.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busa-sdk",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
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",
@@ -22,6 +22,14 @@
22
22
  ".": {
23
23
  "types": "./dist/index.d.ts",
24
24
  "default": "./dist/index.js"
25
+ },
26
+ "./oauth": {
27
+ "types": "./dist/oauth.d.ts",
28
+ "default": "./dist/oauth.js"
29
+ },
30
+ "./oauth-node": {
31
+ "types": "./dist/oauth-node.d.ts",
32
+ "default": "./dist/oauth-node.js"
25
33
  }
26
34
  },
27
35
  "files": [