@dontcode2/backend 0.1.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/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @dontcode2/backend
2
+
3
+ The public SDK for the [DontCode](https://www.dontcode.co) backend: a thin, typed
4
+ proxy over the v1 HTTP gateway. Auth, database, and file storage for an app you host
5
+ yourself ("bring your own code"), behind a single project API key.
6
+
7
+ It speaks the exact wire protocol of the gateway, so you can move between the SDK and
8
+ raw HTTP at any time without platform-side changes.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @dontcode2/backend
14
+ # or: npm i @dontcode2/backend
15
+ ```
16
+
17
+ Works in Node 18+ and the browser; it uses the global `fetch`, with no extra runtime
18
+ dependencies.
19
+
20
+ ## Quick start
21
+
22
+ ```ts
23
+ import { dontcode } from '@dontcode2/backend'
24
+
25
+ // apiKey defaults to process.env.DONTCODE_API_KEY,
26
+ // baseUrl defaults to process.env.DONTCODE_API_URL (then backend.dontcode.co).
27
+ const client = dontcode()
28
+
29
+ await client.auth.signup({
30
+ email: 'tester@example.com',
31
+ password: 'a-strong-password',
32
+ role: 'editor',
33
+ })
34
+ ```
35
+
36
+ You can also pass them explicitly:
37
+
38
+ ```ts
39
+ const client = dontcode({
40
+ apiKey: 'dc_…',
41
+ baseUrl: 'https://backend.dontcode.co',
42
+ })
43
+ ```
44
+
45
+ If no key is set in either place, requests fail naturally with the gateway's
46
+ `401 Missing API key`.
47
+
48
+ ## Auth
49
+
50
+ The API key (`Authorization: Bearer dc_…`) identifies your **project** and is sent on
51
+ every call. The **end-user** access token is separate; pass it as `accessToken` to any
52
+ signed-in call and it travels in the `X-Access-Token` header. Your app owns the session.
53
+
54
+ ```ts
55
+ // Two project settings shape these flows, so handle both states:
56
+ // • email verification: signup may NOT return tokens
57
+ // • MFA: login may be two steps
58
+ const signup = await client.auth.signup({ email, password })
59
+ if (signup.verification_required) {
60
+ await client.auth.verifyEmail({ code }) // 6-digit code from the email
61
+ }
62
+
63
+ const login = await client.auth.login({ email, password })
64
+ if (login.mfa_required) {
65
+ const done = await client.auth.mfa.challenge({
66
+ challengeToken: login.challenge_token!,
67
+ code, // from the authenticator app
68
+ })
69
+ // done.tokens is your session
70
+ } else {
71
+ // login.tokens is your session
72
+ }
73
+
74
+ const { user } = await client.auth.me({ accessToken })
75
+ ```
76
+
77
+ MFA enrollment (signed-in user):
78
+
79
+ ```ts
80
+ const { otpauth_url } = await client.auth.mfa.enroll({ accessToken }) // render as QR
81
+ const { recovery_codes } = await client.auth.mfa.enrollConfirm({ accessToken, code })
82
+ await client.auth.mfa.disable({ accessToken, code })
83
+ ```
84
+
85
+ Also available: `forgotPassword({ email })`, `resetPassword({ code, password })`.
86
+
87
+ ## Database
88
+
89
+ Structured queries only; there is no raw-SQL escape hatch. Schema changes go through
90
+ `migrate`.
91
+
92
+ ```ts
93
+ // db.<table> and db('<table>') are equivalent.
94
+ const rows = await client.db.maps.find({
95
+ where: { ownerId: 'u1', name: { contains: 'demo', mode: 'insensitive' } },
96
+ orderBy: { createdAt: 'desc' },
97
+ limit: 20,
98
+ })
99
+
100
+ const one = await client.db.maps.findFirst({ where: { id: 1 } }) // row | null
101
+ const { id } = await client.db.maps.insert({ name: 'My map' }) // 409 on conflict
102
+ const { count } = await client.db.maps.update({ where: { id }, data: { name: 'Renamed' } })
103
+ await client.db.maps.delete({ where: { id } }) // where is required
104
+ const total = await client.db.maps.count({ where: { ownerId: 'u1' } })
105
+ ```
106
+
107
+ `where` supports direct equality, `null` (IS NULL), arrays (IN), operator objects
108
+ (`equals, not, gt, gte, lt, lte, in, notIn, contains, startsWith, endsWith`, plus
109
+ `mode: 'insensitive'`) and `AND` / `OR` / `NOT` compounds. Unique/foreign-key conflicts
110
+ throw a `409`, the supported idempotency signal (insert, and on 409 treat the row as
111
+ existing).
112
+
113
+ ```ts
114
+ // The one place DDL enters from outside.
115
+ await client.db.migrate({ sql: 'CREATE TABLE IF NOT EXISTS profiles (id uuid primary key);' })
116
+ ```
117
+
118
+ ## Storage
119
+
120
+ ```ts
121
+ const pub = client.storage.public
122
+ const priv = client.storage.private
123
+
124
+ await pub.upload('img/logo.png', fileBlob, 'image/png') // ≤ 100 MB
125
+ pub.getUrl('img/logo.png') // permanent public URL (public bucket only)
126
+
127
+ await priv.list('invoices')
128
+ const { url } = await priv.getTemporaryUrl('invoices/2026.pdf', 600) // signed, expiring
129
+ const { body, contentType } = await priv.download('invoices/2026.pdf') // base64, ≤ 8 MB
130
+ await priv.remove(['invoices/old.pdf'])
131
+ await priv.move('a.pdf', 'archive/a.pdf')
132
+
133
+ // Large files: presign, then PUT the bytes to the returned URL yourself.
134
+ const { url: putUrl } = await priv.presignUpload('big.zip', 'application/zip')
135
+ ```
136
+
137
+ ## Errors
138
+
139
+ Every non-2xx response throws a `DontCodeError`:
140
+
141
+ ```ts
142
+ import { DontCodeError, isDontCodeError } from '@dontcode2/backend'
143
+
144
+ try {
145
+ await client.auth.login({ email, password })
146
+ } catch (err) {
147
+ if (isDontCodeError(err)) {
148
+ err.status // 401, 403, 409, 429, …
149
+ err.code // e.g. 'EmailNotVerified', 'ChallengeExpired'
150
+ err.rateLimited // true on 429
151
+ err.body // the full error envelope
152
+ }
153
+ }
154
+ ```
155
+
156
+ "One more step" auth states (`verification_required`, `mfa_required`) are **successful**
157
+ 2xx responses, not errors; branch on the resolved value for those.
package/dist/index.cjs ADDED
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AuthApi: () => AuthApi,
24
+ BucketClient: () => BucketClient,
25
+ DontCodeError: () => DontCodeError,
26
+ MfaApi: () => MfaApi,
27
+ PublicBucketClient: () => PublicBucketClient,
28
+ TableQuery: () => TableQuery,
29
+ createStorage: () => createStorage,
30
+ dontcode: () => dontcode,
31
+ isDontCodeError: () => isDontCodeError
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // src/auth.ts
36
+ var AUTH_BASE = "/api/v1/auth";
37
+ var MfaApi = class {
38
+ constructor(transport) {
39
+ this.transport = transport;
40
+ }
41
+ /** Complete an MFA login. Pass the `challenge_token` from `login`, plus
42
+ * either the authenticator `code` or a `recoveryCode`. */
43
+ challenge(input) {
44
+ return this.transport.json(`${AUTH_BASE}/mfa/challenge`, {
45
+ challenge_token: input.challengeToken,
46
+ code: input.code,
47
+ recovery_code: input.recoveryCode
48
+ });
49
+ }
50
+ /** Begin enrollment. Render the returned `otpauth_url` as a QR code.
51
+ * Enrollment stays pending until `enrollConfirm`. */
52
+ enroll(input) {
53
+ return this.transport.json(
54
+ `${AUTH_BASE}/mfa/enroll`,
55
+ {},
56
+ { accessToken: input.accessToken }
57
+ );
58
+ }
59
+ /** Confirm enrollment with the first authenticator code. The returned
60
+ * `recovery_codes` are shown once and never again. */
61
+ enrollConfirm(input) {
62
+ return this.transport.json(
63
+ `${AUTH_BASE}/mfa/enroll/confirm`,
64
+ { code: input.code },
65
+ { accessToken: input.accessToken }
66
+ );
67
+ }
68
+ /** Turn MFA off. Proves possession of the second factor via `code` or
69
+ * `recoveryCode`. */
70
+ disable(input) {
71
+ return this.transport.json(
72
+ `${AUTH_BASE}/mfa/disable`,
73
+ { code: input.code, recovery_code: input.recoveryCode },
74
+ { accessToken: input.accessToken }
75
+ );
76
+ }
77
+ };
78
+ var AuthApi = class {
79
+ constructor(transport) {
80
+ this.transport = transport;
81
+ this.mfa = new MfaApi(transport);
82
+ }
83
+ /** Create an account. If the project requires email verification the
84
+ * response has `verification_required: true` and NO tokens; collect a
85
+ * code and call `verifyEmail`, then `login`. */
86
+ signup(input) {
87
+ return this.transport.json(`${AUTH_BASE}/signup`, {
88
+ email: input.email,
89
+ password: input.password,
90
+ name: input.name,
91
+ role: input.role
92
+ });
93
+ }
94
+ /** Authenticate. Branch on `mfa_required`: when true you hold only a
95
+ * challenge (finish via `mfa.challenge`); otherwise `tokens` is your
96
+ * session. A 403 `EmailNotVerified` means the email step isn't done. */
97
+ login(input) {
98
+ return this.transport.json(`${AUTH_BASE}/login`, {
99
+ email: input.email,
100
+ password: input.password
101
+ });
102
+ }
103
+ /** Resolve the signed-in user from their access token, or `{ user: null }`. */
104
+ me(input) {
105
+ return this.transport.json(`${AUTH_BASE}/me`, {}, { accessToken: input.accessToken });
106
+ }
107
+ /** Confirm the 6-digit code emailed at signup. */
108
+ verifyEmail(input) {
109
+ return this.transport.json(`${AUTH_BASE}/verify-email`, {
110
+ code: input.code,
111
+ email: input.email
112
+ });
113
+ }
114
+ forgotPassword(input) {
115
+ return this.transport.json(`${AUTH_BASE}/forgot-password`, {
116
+ email: input.email
117
+ });
118
+ }
119
+ resetPassword(input) {
120
+ return this.transport.json(`${AUTH_BASE}/reset-password`, {
121
+ code: input.code,
122
+ password: input.password,
123
+ email: input.email
124
+ });
125
+ }
126
+ };
127
+
128
+ // src/db.ts
129
+ var DB_PATH = "/api/v1/db";
130
+ var MIGRATE_PATH = "/api/v1/db/migrate";
131
+ var TableQuery = class {
132
+ constructor(transport, tableName) {
133
+ this.transport = transport;
134
+ this.tableName = tableName;
135
+ }
136
+ async run(operation, options) {
137
+ const res = await this.transport.json(DB_PATH, {
138
+ operation,
139
+ tableName: this.tableName,
140
+ options
141
+ });
142
+ return res.data;
143
+ }
144
+ /** Rows matching the query (max 1000 per call). */
145
+ find(options = {}) {
146
+ return this.run("find", options);
147
+ }
148
+ /** Alias of `find`. */
149
+ findMany(options = {}) {
150
+ return this.run("findMany", options);
151
+ }
152
+ /** The first matching row, or `null`. */
153
+ findFirst(options = {}) {
154
+ return this.run("findFirst", options);
155
+ }
156
+ /** Alias of `findFirst`. */
157
+ findOne(options = {}) {
158
+ return this.run("findOne", options);
159
+ }
160
+ /** Insert one row. Returns `{ id }`. Unique/FK conflicts throw a 409
161
+ * DontCodeError, the supported idempotency signal. */
162
+ insert(data) {
163
+ return this.run("insert", { data });
164
+ }
165
+ /** Update rows matching `where`. Returns `{ count }`. */
166
+ update(input) {
167
+ return this.run("update", { where: input.where, data: input.data });
168
+ }
169
+ /** Delete rows matching `where`. Returns `{ count }`. */
170
+ delete(input) {
171
+ return this.run("delete", { where: input.where });
172
+ }
173
+ /** Count matching rows. */
174
+ count(options = {}) {
175
+ return this.run("count", options);
176
+ }
177
+ };
178
+ function createDb(transport) {
179
+ const table = (tableName) => new TableQuery(transport, tableName);
180
+ const migrate = (input) => transport.json(MIGRATE_PATH, { sql: input.sql });
181
+ return new Proxy(table, {
182
+ get(target, prop, receiver) {
183
+ if (prop === "migrate") return migrate;
184
+ if (typeof prop !== "string" || prop === "then" || prop in target) {
185
+ return Reflect.get(target, prop, receiver);
186
+ }
187
+ return new TableQuery(transport, prop);
188
+ },
189
+ apply(_target, _thisArg, args) {
190
+ return table(args[0]);
191
+ }
192
+ });
193
+ }
194
+
195
+ // src/errors.ts
196
+ var DontCodeError = class extends Error {
197
+ constructor(status, body) {
198
+ const message = typeof body?.error === "string" && body.error.length > 0 ? body.error : `DontCode request failed with status ${status}`;
199
+ super(message);
200
+ this.name = "DontCodeError";
201
+ this.status = status;
202
+ this.code = typeof body?.code === "string" ? body.code : void 0;
203
+ this.body = body ?? {};
204
+ }
205
+ /** True when the request was rejected by the per-key rate limiter. */
206
+ get rateLimited() {
207
+ return this.status === 429;
208
+ }
209
+ };
210
+ function isDontCodeError(err) {
211
+ if (err instanceof DontCodeError) return true;
212
+ return typeof err === "object" && err !== null && err.name === "DontCodeError";
213
+ }
214
+
215
+ // src/http.ts
216
+ var Transport = class {
217
+ constructor(config) {
218
+ this.config = config;
219
+ }
220
+ headers(opts) {
221
+ const headers = {};
222
+ if (this.config.apiKey) headers["Authorization"] = `Bearer ${this.config.apiKey}`;
223
+ if (opts?.accessToken) headers["X-Access-Token"] = opts.accessToken;
224
+ return headers;
225
+ }
226
+ url(path) {
227
+ return `${this.config.baseUrl}${path}`;
228
+ }
229
+ /** POST a JSON body and parse the JSON response. */
230
+ async json(path, body, opts) {
231
+ const res = await fetch(this.url(path), {
232
+ method: "POST",
233
+ headers: { ...this.headers(opts), "Content-Type": "application/json" },
234
+ body: JSON.stringify(body ?? {})
235
+ });
236
+ return this.parse(res);
237
+ }
238
+ /** PUT a multipart form (file uploads). The runtime sets the boundary. */
239
+ async multipart(path, form, opts) {
240
+ const res = await fetch(this.url(path), {
241
+ method: "PUT",
242
+ headers: this.headers(opts),
243
+ body: form
244
+ });
245
+ return this.parse(res);
246
+ }
247
+ async parse(res) {
248
+ const raw = await res.text();
249
+ let data = null;
250
+ if (raw) {
251
+ try {
252
+ data = JSON.parse(raw);
253
+ } catch {
254
+ data = { error: raw };
255
+ }
256
+ }
257
+ if (!res.ok) {
258
+ const body = data && typeof data === "object" ? data : { error: res.statusText || "Request failed" };
259
+ throw new DontCodeError(res.status, body);
260
+ }
261
+ return data;
262
+ }
263
+ };
264
+
265
+ // src/storage.ts
266
+ var STORAGE_PATH = "/api/v1/storage";
267
+ var DEFAULT_CONTENT_TYPE = "application/octet-stream";
268
+ function toBlob(body, contentType) {
269
+ if (body instanceof Blob) return body;
270
+ if (typeof body === "string") return new Blob([body], { type: contentType });
271
+ if (body instanceof ArrayBuffer) return new Blob([body], { type: contentType });
272
+ if (ArrayBuffer.isView(body)) {
273
+ return new Blob([body], { type: contentType });
274
+ }
275
+ throw new TypeError("upload expects a Blob, ArrayBuffer, typed array, or string");
276
+ }
277
+ function fileName(path) {
278
+ return path.split("/").filter(Boolean).pop() ?? path;
279
+ }
280
+ var BucketClient = class {
281
+ constructor(transport, bucket) {
282
+ this.transport = transport;
283
+ this.bucket = bucket;
284
+ }
285
+ op(operation, params = {}) {
286
+ return this.transport.json(STORAGE_PATH, { operation, bucket: this.bucket, ...params });
287
+ }
288
+ /** List objects under `prefix`. */
289
+ list(prefix) {
290
+ return this.op("list", { prefix });
291
+ }
292
+ /** Delete one or more objects. Returns `{ deleted }`. */
293
+ remove(paths) {
294
+ return this.op("remove", { paths });
295
+ }
296
+ /** Move/rename an object within the bucket. */
297
+ move(from, to) {
298
+ return this.op("move", { from, to });
299
+ }
300
+ createFolder(path) {
301
+ return this.op("createFolder", { path });
302
+ }
303
+ /** Download an object inline (≤ 8 MB). Use `getTemporaryUrl` for larger files. */
304
+ download(path) {
305
+ return this.op("download", { path });
306
+ }
307
+ /** A short-lived signed URL (default 300s, max 7 days). */
308
+ getTemporaryUrl(path, expiresIn) {
309
+ return this.op("getTemporaryUrl", { path, expiresIn });
310
+ }
311
+ /** A presigned PUT URL for direct, large uploads (≤ no inline limit). */
312
+ presignUpload(path, contentType) {
313
+ return this.op("presignUpload", { path, contentType });
314
+ }
315
+ /** Upload bytes directly (≤ 100 MB). For larger files, `presignUpload`
316
+ * then PUT to the returned URL yourself. */
317
+ upload(path, body, contentType = DEFAULT_CONTENT_TYPE) {
318
+ const form = new FormData();
319
+ form.append("file", toBlob(body, contentType), fileName(path));
320
+ form.append("bucket", this.bucket);
321
+ form.append("path", path);
322
+ form.append("contentType", contentType);
323
+ return this.transport.multipart(STORAGE_PATH, form);
324
+ }
325
+ };
326
+ var PublicBucketClient = class extends BucketClient {
327
+ constructor(transport) {
328
+ super(transport, "public");
329
+ }
330
+ /** The permanent public URL for an object. */
331
+ getUrl(path) {
332
+ return this.op("getUrl", { path });
333
+ }
334
+ };
335
+ function createStorage(transport) {
336
+ return {
337
+ public: new PublicBucketClient(transport),
338
+ private: new BucketClient(transport, "private")
339
+ };
340
+ }
341
+
342
+ // src/client.ts
343
+ var DEFAULT_BASE_URL = "https://backend.dontcode.co";
344
+ function fromEnv(name) {
345
+ if (typeof process === "undefined" || !process.env) return void 0;
346
+ return process.env[name];
347
+ }
348
+ function dontcode(options = {}) {
349
+ const apiKey = options.apiKey ?? fromEnv("DONTCODE_API_KEY");
350
+ const baseUrl = (options.baseUrl ?? fromEnv("DONTCODE_API_URL") ?? DEFAULT_BASE_URL).replace(
351
+ /\/+$/,
352
+ ""
353
+ );
354
+ const transport = new Transport({ apiKey, baseUrl });
355
+ return {
356
+ auth: new AuthApi(transport),
357
+ db: createDb(transport),
358
+ storage: createStorage(transport)
359
+ };
360
+ }
361
+ // Annotate the CommonJS export names for ESM import in node:
362
+ 0 && (module.exports = {
363
+ AuthApi,
364
+ BucketClient,
365
+ DontCodeError,
366
+ MfaApi,
367
+ PublicBucketClient,
368
+ TableQuery,
369
+ createStorage,
370
+ dontcode,
371
+ isDontCodeError
372
+ });
373
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/auth.ts","../src/db.ts","../src/errors.ts","../src/http.ts","../src/storage.ts","../src/client.ts"],"sourcesContent":["export { dontcode } from './client'\nexport type { DontCodeClient, DontCodeClientOptions } from './client'\n\nexport { DontCodeError, isDontCodeError } from './errors'\nexport type { DontCodeErrorBody } from './errors'\n\nexport { AuthApi, MfaApi } from './auth'\nexport { TableQuery, type DbClient } from './db'\nexport { BucketClient, PublicBucketClient, createStorage, type StorageClient } from './storage'\n\nexport type * from './types'\n","import { Transport } from './http'\nimport type {\n ForgotPasswordInput,\n LoginInput,\n LoginResult,\n MeResult,\n MfaChallengeInput,\n MfaDisableInput,\n MfaEnrollConfirmInput,\n MfaEnrollResult,\n ResetPasswordInput,\n SignupInput,\n SignupResult,\n SimpleResult,\n VerifyEmailInput,\n} from './types'\n\nconst AUTH_BASE = '/api/v1/auth'\n\n/**\n * MFA is per-user and opt-in. `enroll`/`enrollConfirm`/`disable` act as the\n * signed-in user, so they need the end-user access token. `challenge` does\n * not; it completes a login that returned `mfa_required`, exchanging the\n * short-lived challenge token for real session tokens.\n */\nexport class MfaApi {\n constructor(private readonly transport: Transport) {}\n\n /** Complete an MFA login. Pass the `challenge_token` from `login`, plus\n * either the authenticator `code` or a `recoveryCode`. */\n challenge(input: MfaChallengeInput): Promise<LoginResult> {\n return this.transport.json<LoginResult>(`${AUTH_BASE}/mfa/challenge`, {\n challenge_token: input.challengeToken,\n code: input.code,\n recovery_code: input.recoveryCode,\n })\n }\n\n /** Begin enrollment. Render the returned `otpauth_url` as a QR code.\n * Enrollment stays pending until `enrollConfirm`. */\n enroll(input: { accessToken: string }): Promise<MfaEnrollResult> {\n return this.transport.json<MfaEnrollResult>(\n `${AUTH_BASE}/mfa/enroll`,\n {},\n { accessToken: input.accessToken }\n )\n }\n\n /** Confirm enrollment with the first authenticator code. The returned\n * `recovery_codes` are shown once and never again. */\n enrollConfirm(input: MfaEnrollConfirmInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(\n `${AUTH_BASE}/mfa/enroll/confirm`,\n { code: input.code },\n { accessToken: input.accessToken }\n )\n }\n\n /** Turn MFA off. Proves possession of the second factor via `code` or\n * `recoveryCode`. */\n disable(input: MfaDisableInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(\n `${AUTH_BASE}/mfa/disable`,\n { code: input.code, recovery_code: input.recoveryCode },\n { accessToken: input.accessToken }\n )\n }\n}\n\n/**\n * Fronts DontCode Auth with the same shapes as the gateway. Two behaviours are\n * project settings (not API flags) and your code must handle both states:\n * email verification (signup may not return tokens) and MFA (login may be two\n * steps). Branch on the resolved value; never assume one round-trip.\n */\nexport class AuthApi {\n readonly mfa: MfaApi\n\n constructor(private readonly transport: Transport) {\n this.mfa = new MfaApi(transport)\n }\n\n /** Create an account. If the project requires email verification the\n * response has `verification_required: true` and NO tokens; collect a\n * code and call `verifyEmail`, then `login`. */\n signup(input: SignupInput): Promise<SignupResult> {\n return this.transport.json<SignupResult>(`${AUTH_BASE}/signup`, {\n email: input.email,\n password: input.password,\n name: input.name,\n role: input.role,\n })\n }\n\n /** Authenticate. Branch on `mfa_required`: when true you hold only a\n * challenge (finish via `mfa.challenge`); otherwise `tokens` is your\n * session. A 403 `EmailNotVerified` means the email step isn't done. */\n login(input: LoginInput): Promise<LoginResult> {\n return this.transport.json<LoginResult>(`${AUTH_BASE}/login`, {\n email: input.email,\n password: input.password,\n })\n }\n\n /** Resolve the signed-in user from their access token, or `{ user: null }`. */\n me(input: { accessToken: string }): Promise<MeResult> {\n return this.transport.json<MeResult>(`${AUTH_BASE}/me`, {}, { accessToken: input.accessToken })\n }\n\n /** Confirm the 6-digit code emailed at signup. */\n verifyEmail(input: VerifyEmailInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/verify-email`, {\n code: input.code,\n email: input.email,\n })\n }\n\n forgotPassword(input: ForgotPasswordInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/forgot-password`, {\n email: input.email,\n })\n }\n\n resetPassword(input: ResetPasswordInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/reset-password`, {\n code: input.code,\n password: input.password,\n email: input.email,\n })\n }\n}\n","import { Transport } from './http'\nimport type {\n DeleteInput,\n MigrateInput,\n MigrateResult,\n QueryOptions,\n UpdateInput,\n} from './types'\n\nconst DB_PATH = '/api/v1/db'\nconst MIGRATE_PATH = '/api/v1/db/migrate'\n\n/** The gateway wraps every DB result in `{ data }`; we unwrap it. */\ninterface DbEnvelope<T> {\n data: T\n}\n\n/**\n * A handle to one table. Structured queries only; there is no raw-SQL escape\n * hatch (schema changes go through `db.migrate`). `update` and `delete`\n * require a `where` clause server-side, so the types make it mandatory.\n */\nexport class TableQuery {\n constructor(\n private readonly transport: Transport,\n private readonly tableName: string\n ) {}\n\n private async run<T>(operation: string, options: unknown): Promise<T> {\n const res = await this.transport.json<DbEnvelope<T>>(DB_PATH, {\n operation,\n tableName: this.tableName,\n options,\n })\n return res.data\n }\n\n /** Rows matching the query (max 1000 per call). */\n find<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T[]> {\n return this.run<T[]>('find', options)\n }\n\n /** Alias of `find`. */\n findMany<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T[]> {\n return this.run<T[]>('findMany', options)\n }\n\n /** The first matching row, or `null`. */\n findFirst<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T | null> {\n return this.run<T | null>('findFirst', options)\n }\n\n /** Alias of `findFirst`. */\n findOne<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T | null> {\n return this.run<T | null>('findOne', options)\n }\n\n /** Insert one row. Returns `{ id }`. Unique/FK conflicts throw a 409\n * DontCodeError, the supported idempotency signal. */\n insert(data: Record<string, unknown>): Promise<{ id: unknown }> {\n return this.run<{ id: unknown }>('insert', { data })\n }\n\n /** Update rows matching `where`. Returns `{ count }`. */\n update(input: UpdateInput): Promise<{ count: number }> {\n return this.run<{ count: number }>('update', { where: input.where, data: input.data })\n }\n\n /** Delete rows matching `where`. Returns `{ count }`. */\n delete(input: DeleteInput): Promise<{ count: number }> {\n return this.run<{ count: number }>('delete', { where: input.where })\n }\n\n /** Count matching rows. */\n count(options: Pick<QueryOptions, 'where'> = {}): Promise<number> {\n return this.run<number>('count', options)\n }\n}\n\n/**\n * `db.users.find()` and `db('users').find()` both work; the bracket/callable\n * form is there for table names that aren't valid identifiers. `db.migrate()`\n * applies schema DDL (the one place migrations enter from outside).\n */\nexport type DbClient = {\n readonly [tableName: string]: TableQuery\n} & {\n (tableName: string): TableQuery\n migrate(input: MigrateInput): Promise<MigrateResult>\n}\n\nexport function createDb(transport: Transport): DbClient {\n const table = (tableName: string): TableQuery => new TableQuery(transport, tableName)\n const migrate = (input: MigrateInput): Promise<MigrateResult> =>\n transport.json<MigrateResult>(MIGRATE_PATH, { sql: input.sql })\n\n return new Proxy(table, {\n get(target, prop, receiver) {\n if (prop === 'migrate') return migrate\n // Don't manufacture a TableQuery for symbols or promise-unwrapping\n // probes (`then`) or the function's own members.\n if (typeof prop !== 'string' || prop === 'then' || prop in target) {\n return Reflect.get(target, prop, receiver)\n }\n return new TableQuery(transport, prop)\n },\n apply(_target, _thisArg, args: [string]) {\n return table(args[0])\n },\n }) as unknown as DbClient\n}\n","/**\n * Every non-2xx response from the gateway surfaces as a DontCodeError. The\n * platform's error envelope is `{ error, ... }`, sometimes with a machine\n * `code` (e.g. `EmailNotVerified`, `ChallengeExpired`, `MfaNotOffered`) or\n * rate-limit fields. We preserve the whole body so callers can branch on it.\n *\n * Note: many \"one more step\" auth states (signup needing email verification,\n * login returning `mfa_required`) are 2xx successes, NOT errors; inspect the\n * resolved value for those. Errors are reserved for actual failures.\n */\nexport interface DontCodeErrorBody {\n error?: string\n /** Stable machine code, when the platform sends one. */\n code?: string\n /** Present on 429 responses. */\n rate_limit?: boolean\n /** Seconds until the rate limit resets, on 429 responses. */\n timeleft?: number\n [key: string]: unknown\n}\n\nexport class DontCodeError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number\n /** Stable machine code, when present (e.g. `EmailNotVerified`). */\n readonly code?: string\n /** The raw parsed response body. */\n readonly body: DontCodeErrorBody\n\n constructor(status: number, body: DontCodeErrorBody) {\n const message =\n typeof body?.error === 'string' && body.error.length > 0\n ? body.error\n : `DontCode request failed with status ${status}`\n super(message)\n this.name = 'DontCodeError'\n this.status = status\n this.code = typeof body?.code === 'string' ? body.code : undefined\n this.body = body ?? {}\n }\n\n /** True when the request was rejected by the per-key rate limiter. */\n get rateLimited(): boolean {\n return this.status === 429\n }\n}\n\n/** Cross-bundle-safe check; works even if two copies of the SDK are loaded. */\nexport function isDontCodeError(err: unknown): err is DontCodeError {\n if (err instanceof DontCodeError) return true\n return (\n typeof err === 'object' &&\n err !== null &&\n (err as { name?: unknown }).name === 'DontCodeError'\n )\n}\n","import { DontCodeError, type DontCodeErrorBody } from './errors'\n\nexport interface TransportConfig {\n /** Project API key. When absent, no Authorization header is sent and the\n * gateway responds with its own \"Missing API key\" 401. */\n apiKey?: string\n /** Gateway origin, already normalized (no trailing slash). */\n baseUrl: string\n}\n\nexport interface RequestOptions {\n /** End-user access token, sent as `X-Access-Token` (separate from the\n * project API key). Required by signed-in auth calls. */\n accessToken?: string\n}\n\n/**\n * The single place network requests are made. Everything else in the SDK is a\n * typed shape around `json()` / `multipart()`. No retries, no caching, just a\n * faithful proxy of the v1 gateway.\n */\nexport class Transport {\n constructor(private readonly config: TransportConfig) {}\n\n private headers(opts?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {}\n if (this.config.apiKey) headers['Authorization'] = `Bearer ${this.config.apiKey}`\n if (opts?.accessToken) headers['X-Access-Token'] = opts.accessToken\n return headers\n }\n\n private url(path: string): string {\n return `${this.config.baseUrl}${path}`\n }\n\n /** POST a JSON body and parse the JSON response. */\n async json<T>(path: string, body?: unknown, opts?: RequestOptions): Promise<T> {\n const res = await fetch(this.url(path), {\n method: 'POST',\n headers: { ...this.headers(opts), 'Content-Type': 'application/json' },\n body: JSON.stringify(body ?? {}),\n })\n return this.parse<T>(res)\n }\n\n /** PUT a multipart form (file uploads). The runtime sets the boundary. */\n async multipart<T>(path: string, form: FormData, opts?: RequestOptions): Promise<T> {\n const res = await fetch(this.url(path), {\n method: 'PUT',\n headers: this.headers(opts),\n body: form,\n })\n return this.parse<T>(res)\n }\n\n private async parse<T>(res: Response): Promise<T> {\n const raw = await res.text()\n let data: unknown = null\n if (raw) {\n try {\n data = JSON.parse(raw)\n } catch {\n data = { error: raw }\n }\n }\n if (!res.ok) {\n const body: DontCodeErrorBody =\n data && typeof data === 'object'\n ? (data as DontCodeErrorBody)\n : { error: res.statusText || 'Request failed' }\n throw new DontCodeError(res.status, body)\n }\n return data as T\n }\n}\n","import { Transport } from './http'\nimport type {\n DownloadResult,\n ListResult,\n PresignResult,\n StorageBucket,\n StorageObject,\n TemporaryUrlResult,\n UploadBody,\n} from './types'\n\nconst STORAGE_PATH = '/api/v1/storage'\n\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream'\n\n/** Normalize whatever the caller hands us into a Blob for multipart upload. */\nfunction toBlob(body: UploadBody, contentType: string): Blob {\n if (body instanceof Blob) return body\n if (typeof body === 'string') return new Blob([body], { type: contentType })\n if (body instanceof ArrayBuffer) return new Blob([body], { type: contentType })\n if (ArrayBuffer.isView(body)) {\n return new Blob([body as unknown as BlobPart], { type: contentType })\n }\n throw new TypeError('upload expects a Blob, ArrayBuffer, typed array, or string')\n}\n\nfunction fileName(path: string): string {\n return path.split('/').filter(Boolean).pop() ?? path\n}\n\n/** Operations available on both buckets. */\nexport class BucketClient {\n constructor(\n protected readonly transport: Transport,\n protected readonly bucket: StorageBucket\n ) {}\n\n protected op<T>(operation: string, params: Record<string, unknown> = {}): Promise<T> {\n return this.transport.json<T>(STORAGE_PATH, { operation, bucket: this.bucket, ...params })\n }\n\n /** List objects under `prefix`. */\n list(prefix?: string): Promise<ListResult> {\n return this.op<ListResult>('list', { prefix })\n }\n\n /** Delete one or more objects. Returns `{ deleted }`. */\n remove(paths: string[]): Promise<{ deleted: number }> {\n return this.op<{ deleted: number }>('remove', { paths })\n }\n\n /** Move/rename an object within the bucket. */\n move(from: string, to: string): Promise<{ object: StorageObject }> {\n return this.op<{ object: StorageObject }>('move', { from, to })\n }\n\n createFolder(path: string): Promise<{ created: string }> {\n return this.op<{ created: string }>('createFolder', { path })\n }\n\n /** Download an object inline (≤ 8 MB). Use `getTemporaryUrl` for larger files. */\n download(path: string): Promise<DownloadResult> {\n return this.op<DownloadResult>('download', { path })\n }\n\n /** A short-lived signed URL (default 300s, max 7 days). */\n getTemporaryUrl(path: string, expiresIn?: number): Promise<TemporaryUrlResult> {\n return this.op<TemporaryUrlResult>('getTemporaryUrl', { path, expiresIn })\n }\n\n /** A presigned PUT URL for direct, large uploads (≤ no inline limit). */\n presignUpload(path: string, contentType?: string): Promise<PresignResult> {\n return this.op<PresignResult>('presignUpload', { path, contentType })\n }\n\n /** Upload bytes directly (≤ 100 MB). For larger files, `presignUpload`\n * then PUT to the returned URL yourself. */\n upload(\n path: string,\n body: UploadBody,\n contentType: string = DEFAULT_CONTENT_TYPE\n ): Promise<{ object: StorageObject }> {\n const form = new FormData()\n form.append('file', toBlob(body, contentType), fileName(path))\n form.append('bucket', this.bucket)\n form.append('path', path)\n form.append('contentType', contentType)\n return this.transport.multipart<{ object: StorageObject }>(STORAGE_PATH, form)\n }\n}\n\n/** The public bucket additionally exposes stable public URLs. */\nexport class PublicBucketClient extends BucketClient {\n constructor(transport: Transport) {\n super(transport, 'public')\n }\n\n /** The permanent public URL for an object. */\n getUrl(path: string): Promise<{ url: string }> {\n return this.op<{ url: string }>('getUrl', { path })\n }\n}\n\nexport interface StorageClient {\n public: PublicBucketClient\n private: BucketClient\n}\n\nexport function createStorage(transport: Transport): StorageClient {\n return {\n public: new PublicBucketClient(transport),\n private: new BucketClient(transport, 'private'),\n }\n}\n","import { AuthApi } from './auth'\nimport { createDb, type DbClient } from './db'\nimport { Transport } from './http'\nimport { createStorage, type StorageClient } from './storage'\n\nconst DEFAULT_BASE_URL = 'https://backend.dontcode.co'\n\nexport interface DontCodeClientOptions {\n /** Project API key (`dc_…`). Defaults to `process.env.DONTCODE_API_KEY`.\n * If neither is set, requests fail naturally with the gateway's\n * \"Missing API key\" 401. */\n apiKey?: string\n /** Gateway origin. Defaults to `process.env.DONTCODE_API_URL`, then to\n * `https://backend.dontcode.co`. */\n baseUrl?: string\n}\n\nexport interface DontCodeClient {\n auth: AuthApi\n db: DbClient\n storage: StorageClient\n}\n\n/** Read an env var without assuming `process` exists (e.g. in the browser). */\nfunction fromEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined\n return process.env[name]\n}\n\n/**\n * Create a DontCode backend client. A thin, typed proxy over the v1 HTTP\n * gateway: auth, database, and storage. The API key scopes every request to\n * a single project; there is nothing else to configure.\n *\n * ```ts\n * import { dontcode } from '@dontcode2/backend'\n * const client = dontcode() // reads DONTCODE_API_KEY\n * await client.auth.signup({ email, password, role: 'editor' })\n * ```\n */\nexport function dontcode(options: DontCodeClientOptions = {}): DontCodeClient {\n const apiKey = options.apiKey ?? fromEnv('DONTCODE_API_KEY')\n const baseUrl = (options.baseUrl ?? fromEnv('DONTCODE_API_URL') ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n )\n\n const transport = new Transport({ apiKey, baseUrl })\n\n return {\n auth: new AuthApi(transport),\n db: createDb(transport),\n storage: createStorage(transport),\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiBA,IAAM,YAAY;AAQX,IAAM,SAAN,MAAa;AAAA,EAChB,YAA6B,WAAsB;AAAtB;AAAA,EAAuB;AAAA;AAAA;AAAA,EAIpD,UAAU,OAAgD;AACtD,WAAO,KAAK,UAAU,KAAkB,GAAG,SAAS,kBAAkB;AAAA,MAClE,iBAAiB,MAAM;AAAA,MACvB,MAAM,MAAM;AAAA,MACZ,eAAe,MAAM;AAAA,IACzB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA,EAIA,OAAO,OAA0D;AAC7D,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,CAAC;AAAA,MACD,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA,EAIA,cAAc,OAAqD;AAC/D,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,EAAE,MAAM,MAAM,KAAK;AAAA,MACnB,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA,EAIA,QAAQ,OAA+C;AACnD,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,EAAE,MAAM,MAAM,MAAM,eAAe,MAAM,aAAa;AAAA,MACtD,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AACJ;AAQO,IAAM,UAAN,MAAc;AAAA,EAGjB,YAA6B,WAAsB;AAAtB;AACzB,SAAK,MAAM,IAAI,OAAO,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAA2C;AAC9C,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,WAAW;AAAA,MAC5D,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IAChB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAyC;AAC3C,WAAO,KAAK,UAAU,KAAkB,GAAG,SAAS,UAAU;AAAA,MAC1D,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,IACpB,CAAC;AAAA,EACL;AAAA;AAAA,EAGA,GAAG,OAAmD;AAClD,WAAO,KAAK,UAAU,KAAe,GAAG,SAAS,OAAO,CAAC,GAAG,EAAE,aAAa,MAAM,YAAY,CAAC;AAAA,EAClG;AAAA;AAAA,EAGA,YAAY,OAAgD;AACxD,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,iBAAiB;AAAA,MAClE,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AAAA,EAEA,eAAe,OAAmD;AAC9D,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,oBAAoB;AAAA,MACrE,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,OAAkD;AAC5D,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,mBAAmB;AAAA,MACpE,MAAM,MAAM;AAAA,MACZ,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AACJ;;;ACzHA,IAAM,UAAU;AAChB,IAAM,eAAe;AAYd,IAAM,aAAN,MAAiB;AAAA,EACpB,YACqB,WACA,WACnB;AAFmB;AACA;AAAA,EAClB;AAAA,EAEH,MAAc,IAAO,WAAmB,SAA8B;AAClE,UAAM,MAAM,MAAM,KAAK,UAAU,KAAoB,SAAS;AAAA,MAC1D;AAAA,MACA,WAAW,KAAK;AAAA,MAChB;AAAA,IACJ,CAAC;AACD,WAAO,IAAI;AAAA,EACf;AAAA;AAAA,EAGA,KAAkC,UAAwB,CAAC,GAAiB;AACxE,WAAO,KAAK,IAAS,QAAQ,OAAO;AAAA,EACxC;AAAA;AAAA,EAGA,SAAsC,UAAwB,CAAC,GAAiB;AAC5E,WAAO,KAAK,IAAS,YAAY,OAAO;AAAA,EAC5C;AAAA;AAAA,EAGA,UAAuC,UAAwB,CAAC,GAAsB;AAClF,WAAO,KAAK,IAAc,aAAa,OAAO;AAAA,EAClD;AAAA;AAAA,EAGA,QAAqC,UAAwB,CAAC,GAAsB;AAChF,WAAO,KAAK,IAAc,WAAW,OAAO;AAAA,EAChD;AAAA;AAAA;AAAA,EAIA,OAAO,MAAyD;AAC5D,WAAO,KAAK,IAAqB,UAAU,EAAE,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA,EAGA,OAAO,OAAgD;AACnD,WAAO,KAAK,IAAuB,UAAU,EAAE,OAAO,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC;AAAA,EACzF;AAAA;AAAA,EAGA,OAAO,OAAgD;AACnD,WAAO,KAAK,IAAuB,UAAU,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,EACvE;AAAA;AAAA,EAGA,MAAM,UAAuC,CAAC,GAAoB;AAC9D,WAAO,KAAK,IAAY,SAAS,OAAO;AAAA,EAC5C;AACJ;AAcO,SAAS,SAAS,WAAgC;AACrD,QAAM,QAAQ,CAAC,cAAkC,IAAI,WAAW,WAAW,SAAS;AACpF,QAAM,UAAU,CAAC,UACb,UAAU,KAAoB,cAAc,EAAE,KAAK,MAAM,IAAI,CAAC;AAElE,SAAO,IAAI,MAAM,OAAO;AAAA,IACpB,IAAI,QAAQ,MAAM,UAAU;AACxB,UAAI,SAAS,UAAW,QAAO;AAG/B,UAAI,OAAO,SAAS,YAAY,SAAS,UAAU,QAAQ,QAAQ;AAC/D,eAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,MAC7C;AACA,aAAO,IAAI,WAAW,WAAW,IAAI;AAAA,IACzC;AAAA,IACA,MAAM,SAAS,UAAU,MAAgB;AACrC,aAAO,MAAM,KAAK,CAAC,CAAC;AAAA,IACxB;AAAA,EACJ,CAAC;AACL;;;ACzFO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAQrC,YAAY,QAAgB,MAAyB;AACjD,UAAM,UACF,OAAO,MAAM,UAAU,YAAY,KAAK,MAAM,SAAS,IACjD,KAAK,QACL,uCAAuC,MAAM;AACvD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO;AACzD,SAAK,OAAO,QAAQ,CAAC;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,cAAuB;AACvB,WAAO,KAAK,WAAW;AAAA,EAC3B;AACJ;AAGO,SAAS,gBAAgB,KAAoC;AAChE,MAAI,eAAe,cAAe,QAAO;AACzC,SACI,OAAO,QAAQ,YACf,QAAQ,QACP,IAA2B,SAAS;AAE7C;;;AClCO,IAAM,YAAN,MAAgB;AAAA,EACnB,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAE/C,QAAQ,MAA+C;AAC3D,UAAM,UAAkC,CAAC;AACzC,QAAI,KAAK,OAAO,OAAQ,SAAQ,eAAe,IAAI,UAAU,KAAK,OAAO,MAAM;AAC/E,QAAI,MAAM,YAAa,SAAQ,gBAAgB,IAAI,KAAK;AACxD,WAAO;AAAA,EACX;AAAA,EAEQ,IAAI,MAAsB;AAC9B,WAAO,GAAG,KAAK,OAAO,OAAO,GAAG,IAAI;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,KAAQ,MAAc,MAAgB,MAAmC;AAC3E,UAAM,MAAM,MAAM,MAAM,KAAK,IAAI,IAAI,GAAG;AAAA,MACpC,QAAQ;AAAA,MACR,SAAS,EAAE,GAAG,KAAK,QAAQ,IAAI,GAAG,gBAAgB,mBAAmB;AAAA,MACrE,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACnC,CAAC;AACD,WAAO,KAAK,MAAS,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,MAAM,UAAa,MAAc,MAAgB,MAAmC;AAChF,UAAM,MAAM,MAAM,MAAM,KAAK,IAAI,IAAI,GAAG;AAAA,MACpC,QAAQ;AAAA,MACR,SAAS,KAAK,QAAQ,IAAI;AAAA,MAC1B,MAAM;AAAA,IACV,CAAC;AACD,WAAO,KAAK,MAAS,GAAG;AAAA,EAC5B;AAAA,EAEA,MAAc,MAAS,KAA2B;AAC9C,UAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,QAAI,OAAgB;AACpB,QAAI,KAAK;AACL,UAAI;AACA,eAAO,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACJ,eAAO,EAAE,OAAO,IAAI;AAAA,MACxB;AAAA,IACJ;AACA,QAAI,CAAC,IAAI,IAAI;AACT,YAAM,OACF,QAAQ,OAAO,SAAS,WACjB,OACD,EAAE,OAAO,IAAI,cAAc,iBAAiB;AACtD,YAAM,IAAI,cAAc,IAAI,QAAQ,IAAI;AAAA,IAC5C;AACA,WAAO;AAAA,EACX;AACJ;;;AC/DA,IAAM,eAAe;AAErB,IAAM,uBAAuB;AAG7B,SAAS,OAAO,MAAkB,aAA2B;AACzD,MAAI,gBAAgB,KAAM,QAAO;AACjC,MAAI,OAAO,SAAS,SAAU,QAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAC3E,MAAI,gBAAgB,YAAa,QAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAC9E,MAAI,YAAY,OAAO,IAAI,GAAG;AAC1B,WAAO,IAAI,KAAK,CAAC,IAA2B,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,EACxE;AACA,QAAM,IAAI,UAAU,4DAA4D;AACpF;AAEA,SAAS,SAAS,MAAsB;AACpC,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AACpD;AAGO,IAAM,eAAN,MAAmB;AAAA,EACtB,YACuB,WACA,QACrB;AAFqB;AACA;AAAA,EACpB;AAAA,EAEO,GAAM,WAAmB,SAAkC,CAAC,GAAe;AACjF,WAAO,KAAK,UAAU,KAAQ,cAAc,EAAE,WAAW,QAAQ,KAAK,QAAQ,GAAG,OAAO,CAAC;AAAA,EAC7F;AAAA;AAAA,EAGA,KAAK,QAAsC;AACvC,WAAO,KAAK,GAAe,QAAQ,EAAE,OAAO,CAAC;AAAA,EACjD;AAAA;AAAA,EAGA,OAAO,OAA+C;AAClD,WAAO,KAAK,GAAwB,UAAU,EAAE,MAAM,CAAC;AAAA,EAC3D;AAAA;AAAA,EAGA,KAAK,MAAc,IAAgD;AAC/D,WAAO,KAAK,GAA8B,QAAQ,EAAE,MAAM,GAAG,CAAC;AAAA,EAClE;AAAA,EAEA,aAAa,MAA4C;AACrD,WAAO,KAAK,GAAwB,gBAAgB,EAAE,KAAK,CAAC;AAAA,EAChE;AAAA;AAAA,EAGA,SAAS,MAAuC;AAC5C,WAAO,KAAK,GAAmB,YAAY,EAAE,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA,EAGA,gBAAgB,MAAc,WAAiD;AAC3E,WAAO,KAAK,GAAuB,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAAA,EAC7E;AAAA;AAAA,EAGA,cAAc,MAAc,aAA8C;AACtE,WAAO,KAAK,GAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAAA,EACxE;AAAA;AAAA;AAAA,EAIA,OACI,MACA,MACA,cAAsB,sBACY;AAClC,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,QAAQ,OAAO,MAAM,WAAW,GAAG,SAAS,IAAI,CAAC;AAC7D,SAAK,OAAO,UAAU,KAAK,MAAM;AACjC,SAAK,OAAO,QAAQ,IAAI;AACxB,SAAK,OAAO,eAAe,WAAW;AACtC,WAAO,KAAK,UAAU,UAAqC,cAAc,IAAI;AAAA,EACjF;AACJ;AAGO,IAAM,qBAAN,cAAiC,aAAa;AAAA,EACjD,YAAY,WAAsB;AAC9B,UAAM,WAAW,QAAQ;AAAA,EAC7B;AAAA;AAAA,EAGA,OAAO,MAAwC;AAC3C,WAAO,KAAK,GAAoB,UAAU,EAAE,KAAK,CAAC;AAAA,EACtD;AACJ;AAOO,SAAS,cAAc,WAAqC;AAC/D,SAAO;AAAA,IACH,QAAQ,IAAI,mBAAmB,SAAS;AAAA,IACxC,SAAS,IAAI,aAAa,WAAW,SAAS;AAAA,EAClD;AACJ;;;AC5GA,IAAM,mBAAmB;AAmBzB,SAAS,QAAQ,MAAkC;AAC/C,MAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,IAAK,QAAO;AAC3D,SAAO,QAAQ,IAAI,IAAI;AAC3B;AAaO,SAAS,SAAS,UAAiC,CAAC,GAAmB;AAC1E,QAAM,SAAS,QAAQ,UAAU,QAAQ,kBAAkB;AAC3D,QAAM,WAAW,QAAQ,WAAW,QAAQ,kBAAkB,KAAK,kBAAkB;AAAA,IACjF;AAAA,IACA;AAAA,EACJ;AAEA,QAAM,YAAY,IAAI,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAEnD,SAAO;AAAA,IACH,MAAM,IAAI,QAAQ,SAAS;AAAA,IAC3B,IAAI,SAAS,SAAS;AAAA,IACtB,SAAS,cAAc,SAAS;AAAA,EACpC;AACJ;","names":[]}