chadstart 1.0.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 (115) hide show
  1. package/.dockerignore +10 -0
  2. package/.env.example +46 -0
  3. package/.github/workflows/browser-test.yml +34 -0
  4. package/.github/workflows/docker-publish.yml +54 -0
  5. package/.github/workflows/docs.yml +31 -0
  6. package/.github/workflows/npm-chadstart.yml +27 -0
  7. package/.github/workflows/npm-sdk.yml +38 -0
  8. package/.github/workflows/test.yml +85 -0
  9. package/.weblate +9 -0
  10. package/Dockerfile +23 -0
  11. package/README.md +348 -0
  12. package/admin/index.html +2802 -0
  13. package/admin/login.html +207 -0
  14. package/chadstart.example.yml +416 -0
  15. package/chadstart.schema.json +367 -0
  16. package/chadstart.yaml +53 -0
  17. package/cli/cli.js +295 -0
  18. package/core/api-generator.js +606 -0
  19. package/core/auth.js +298 -0
  20. package/core/db.js +384 -0
  21. package/core/entity-engine.js +166 -0
  22. package/core/error-reporter.js +132 -0
  23. package/core/file-storage.js +97 -0
  24. package/core/functions-engine.js +353 -0
  25. package/core/openapi.js +171 -0
  26. package/core/plugin-loader.js +92 -0
  27. package/core/realtime.js +93 -0
  28. package/core/schema-validator.js +50 -0
  29. package/core/seeder.js +231 -0
  30. package/core/telemetry.js +119 -0
  31. package/core/upload.js +372 -0
  32. package/core/workers/php_worker.php +19 -0
  33. package/core/workers/python_worker.py +33 -0
  34. package/core/workers/ruby_worker.rb +21 -0
  35. package/core/yaml-loader.js +64 -0
  36. package/demo/chadstart.yaml +178 -0
  37. package/demo/docker-compose.yml +31 -0
  38. package/demo/functions/greet.go +39 -0
  39. package/demo/functions/hello.cpp +18 -0
  40. package/demo/functions/hello.py +13 -0
  41. package/demo/functions/hello.rb +10 -0
  42. package/demo/functions/onTodoCreated.js +13 -0
  43. package/demo/functions/ping.sh +13 -0
  44. package/demo/functions/stats.js +22 -0
  45. package/demo/public/index.html +522 -0
  46. package/docker-compose.yml +17 -0
  47. package/docs/access-policies.md +155 -0
  48. package/docs/admin-ui.md +29 -0
  49. package/docs/angular.md +69 -0
  50. package/docs/astro.md +71 -0
  51. package/docs/auth.md +160 -0
  52. package/docs/cli.md +56 -0
  53. package/docs/config.md +127 -0
  54. package/docs/crud.md +627 -0
  55. package/docs/deploy.md +113 -0
  56. package/docs/docker.md +59 -0
  57. package/docs/entities.md +385 -0
  58. package/docs/functions.md +196 -0
  59. package/docs/getting-started.md +79 -0
  60. package/docs/groups.md +85 -0
  61. package/docs/index.md +5 -0
  62. package/docs/llm-rules.md +81 -0
  63. package/docs/middlewares.md +78 -0
  64. package/docs/overrides/home.html +350 -0
  65. package/docs/plugins.md +59 -0
  66. package/docs/react.md +75 -0
  67. package/docs/realtime.md +43 -0
  68. package/docs/s3-storage.md +40 -0
  69. package/docs/security.md +23 -0
  70. package/docs/stylesheets/extra.css +375 -0
  71. package/docs/svelte.md +71 -0
  72. package/docs/telemetry.md +97 -0
  73. package/docs/upload.md +168 -0
  74. package/docs/validation.md +115 -0
  75. package/docs/vue.md +86 -0
  76. package/docs/webhooks.md +87 -0
  77. package/index.js +11 -0
  78. package/locales/en/admin.json +169 -0
  79. package/mkdocs.yml +82 -0
  80. package/package.json +65 -0
  81. package/playwright.config.js +24 -0
  82. package/public/.gitkeep +0 -0
  83. package/sdk/README.md +284 -0
  84. package/sdk/package.json +39 -0
  85. package/sdk/scripts/build.js +58 -0
  86. package/sdk/src/index.js +368 -0
  87. package/sdk/test/sdk.test.cjs +340 -0
  88. package/sdk/types/index.d.ts +217 -0
  89. package/server/express-server.js +734 -0
  90. package/test/access-policies.test.js +96 -0
  91. package/test/ai.test.js +81 -0
  92. package/test/api-keys.test.js +361 -0
  93. package/test/auth.test.js +122 -0
  94. package/test/browser/admin-ui.spec.js +127 -0
  95. package/test/browser/global-setup.js +71 -0
  96. package/test/browser/global-teardown.js +11 -0
  97. package/test/db.test.js +227 -0
  98. package/test/entity-engine.test.js +193 -0
  99. package/test/error-reporter.test.js +140 -0
  100. package/test/functions-engine.test.js +240 -0
  101. package/test/groups.test.js +212 -0
  102. package/test/hot-reload.test.js +153 -0
  103. package/test/i18n.test.js +173 -0
  104. package/test/middleware.test.js +76 -0
  105. package/test/openapi.test.js +67 -0
  106. package/test/schema-validator.test.js +83 -0
  107. package/test/sdk.test.js +90 -0
  108. package/test/seeder.test.js +279 -0
  109. package/test/settings.test.js +109 -0
  110. package/test/telemetry.test.js +254 -0
  111. package/test/test.js +17 -0
  112. package/test/upload.test.js +265 -0
  113. package/test/validation.test.js +96 -0
  114. package/test/yaml-loader.test.js +93 -0
  115. package/utils/logger.js +24 -0
@@ -0,0 +1,340 @@
1
+ /**
2
+ * SDK unit tests — runs without a live server.
3
+ * Uses Node.js built-in assert and a mock fetch implementation.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const assert = require('node:assert/strict');
9
+
10
+ // ── Inject a mock global fetch before loading the CJS build ──────────────────
11
+ const calls = [];
12
+
13
+ function mockFetch(url, options) {
14
+ calls.push({ url, options });
15
+ const lastMock = mockFetch._queue.shift();
16
+ if (!lastMock) throw new Error(`Unexpected fetch call to ${url}`);
17
+ return Promise.resolve({
18
+ ok: lastMock.ok !== false,
19
+ status: lastMock.status || 200,
20
+ headers: { get: () => 'application/json' },
21
+ json: () => Promise.resolve(lastMock.body),
22
+ text: () => Promise.resolve(JSON.stringify(lastMock.body)),
23
+ });
24
+ }
25
+ mockFetch._queue = [];
26
+
27
+ global.fetch = mockFetch;
28
+
29
+ const Chadstart = require('../dist/index.cjs');
30
+ const { ChadstartError, CollectionQuery, SingleQuery, AuthQuery } = require('../dist/index.cjs');
31
+
32
+ function enqueue(body, ok = true, status = 200) {
33
+ mockFetch._queue.push({ body, ok, status });
34
+ }
35
+
36
+ async function run() {
37
+ let passed = 0;
38
+ let failed = 0;
39
+
40
+ async function test(name, fn) {
41
+ calls.length = 0;
42
+ mockFetch._queue.length = 0;
43
+ try {
44
+ await fn();
45
+ console.log(` ✓ ${name}`);
46
+ passed++;
47
+ } catch (err) {
48
+ console.error(` ✗ ${name}`);
49
+ console.error(` ${err.message}`);
50
+ failed++;
51
+ }
52
+ }
53
+
54
+ console.log('\nChadstart SDK tests\n');
55
+
56
+ // ── Constructor ──────────────────────────────────────────────────────────────
57
+ await test('default baseUrl is http://localhost:3000', () => {
58
+ const c = new Chadstart();
59
+ assert.equal(c._baseUrl, 'http://localhost:3000');
60
+ });
61
+
62
+ await test('custom baseUrl strips trailing slash', () => {
63
+ const c = new Chadstart('http://example.com/');
64
+ assert.equal(c._baseUrl, 'http://example.com');
65
+ });
66
+
67
+ // ── from() returns CollectionQuery ────────────────────────────────────────────
68
+ await test('from() returns a CollectionQuery', () => {
69
+ const c = new Chadstart();
70
+ assert.ok(c.from('posts') instanceof CollectionQuery);
71
+ });
72
+
73
+ await test('single() returns a SingleQuery', () => {
74
+ const c = new Chadstart();
75
+ assert.ok(c.single('homepage') instanceof SingleQuery);
76
+ });
77
+
78
+ await test('auth() returns an AuthQuery', () => {
79
+ const c = new Chadstart();
80
+ assert.ok(c.auth('customers') instanceof AuthQuery);
81
+ });
82
+
83
+ // ── Token management ──────────────────────────────────────────────────────────
84
+ await test('setToken / clearToken', () => {
85
+ const c = new Chadstart();
86
+ c.setToken('abc123');
87
+ assert.equal(c._token, 'abc123');
88
+ c.clearToken();
89
+ assert.equal(c._token, null);
90
+ });
91
+
92
+ // ── Collections — find() ──────────────────────────────────────────────────────
93
+ await test('find() calls GET /api/collections/:slug', async () => {
94
+ const c = new Chadstart('http://test');
95
+ const fakeResult = { data: [{ id: '1', title: 'Hello' }], total: 1, currentPage: 1, lastPage: 1, from: 1, to: 1, perPage: 10 };
96
+ enqueue(fakeResult);
97
+ const result = await c.from('posts').find();
98
+ assert.deepEqual(result, fakeResult);
99
+ assert.equal(calls[0].url, 'http://test/api/collections/posts');
100
+ assert.equal(calls[0].options.method, 'GET');
101
+ });
102
+
103
+ await test('find() appends page and perPage params', async () => {
104
+ const c = new Chadstart('http://test');
105
+ enqueue({ data: [] });
106
+ await c.from('posts').find({ page: 2, perPage: 20 });
107
+ assert.ok(calls[0].url.includes('page=2'));
108
+ assert.ok(calls[0].url.includes('perPage=20'));
109
+ });
110
+
111
+ await test('where() with = builds _eq filter', async () => {
112
+ const c = new Chadstart('http://test');
113
+ enqueue({ data: [] });
114
+ await c.from('posts').where('published = true').find();
115
+ assert.ok(calls[0].url.includes('published_eq=true'), calls[0].url);
116
+ });
117
+
118
+ await test('where() with != builds _neq filter', async () => {
119
+ const c = new Chadstart('http://test');
120
+ enqueue({ data: [] });
121
+ await c.from('posts').where('status != draft').find();
122
+ assert.ok(calls[0].url.includes('status_neq=draft'), calls[0].url);
123
+ });
124
+
125
+ await test('where() with >= builds _gte filter', async () => {
126
+ const c = new Chadstart('http://test');
127
+ enqueue({ data: [] });
128
+ await c.from('products').where('price >= 10').find();
129
+ assert.ok(calls[0].url.includes('price_gte=10'), calls[0].url);
130
+ });
131
+
132
+ await test('where() with <= builds _lte filter', async () => {
133
+ const c = new Chadstart('http://test');
134
+ enqueue({ data: [] });
135
+ await c.from('products').where('price <= 100').find();
136
+ assert.ok(calls[0].url.includes('price_lte=100'), calls[0].url);
137
+ });
138
+
139
+ await test('where() with > builds _gt filter', async () => {
140
+ const c = new Chadstart('http://test');
141
+ enqueue({ data: [] });
142
+ await c.from('products').where('stock > 0').find();
143
+ assert.ok(calls[0].url.includes('stock_gt=0'), calls[0].url);
144
+ });
145
+
146
+ await test('where() with < builds _lt filter', async () => {
147
+ const c = new Chadstart('http://test');
148
+ enqueue({ data: [] });
149
+ await c.from('products').where('stock < 5').find();
150
+ assert.ok(calls[0].url.includes('stock_lt=5'), calls[0].url);
151
+ });
152
+
153
+ await test('where() with like builds _like filter', async () => {
154
+ const c = new Chadstart('http://test');
155
+ enqueue({ data: [] });
156
+ await c.from('posts').where('title like %hello%').find();
157
+ assert.ok(calls[0].url.includes('title_like='), calls[0].url);
158
+ });
159
+
160
+ await test('andWhere() chains multiple filters', async () => {
161
+ const c = new Chadstart('http://test');
162
+ enqueue({ data: [] });
163
+ await c.from('posts').where('published = true').andWhere('status != draft').find();
164
+ assert.ok(calls[0].url.includes('published_eq=true'));
165
+ assert.ok(calls[0].url.includes('status_neq=draft'));
166
+ });
167
+
168
+ await test('orderBy() adds orderBy and order params', async () => {
169
+ const c = new Chadstart('http://test');
170
+ enqueue({ data: [] });
171
+ await c.from('posts').orderBy('createdAt', { desc: true }).find();
172
+ assert.ok(calls[0].url.includes('orderBy=createdAt'));
173
+ assert.ok(calls[0].url.includes('order=DESC'));
174
+ });
175
+
176
+ await test('orderBy() defaults to ASC', async () => {
177
+ const c = new Chadstart('http://test');
178
+ enqueue({ data: [] });
179
+ await c.from('posts').orderBy('title').find();
180
+ assert.ok(calls[0].url.includes('order=ASC'));
181
+ });
182
+
183
+ await test('with() adds relations param', async () => {
184
+ const c = new Chadstart('http://test');
185
+ enqueue({ data: [] });
186
+ await c.from('posts').with(['author', 'tags']).find();
187
+ assert.ok(calls[0].url.includes('relations=author%2Ctags') || calls[0].url.includes('relations=author,tags'), calls[0].url);
188
+ });
189
+
190
+ // ── findOneById() ─────────────────────────────────────────────────────────────
191
+ await test('findOneById() calls GET /api/collections/:slug/:id', async () => {
192
+ const c = new Chadstart('http://test');
193
+ enqueue({ id: '1', title: 'Hello' });
194
+ await c.from('posts').findOneById('1');
195
+ assert.equal(calls[0].url, 'http://test/api/collections/posts/1');
196
+ assert.equal(calls[0].options.method, 'GET');
197
+ });
198
+
199
+ await test('findOneById() with relations appends relations param', async () => {
200
+ const c = new Chadstart('http://test');
201
+ enqueue({ id: '1' });
202
+ await c.from('posts').with(['author']).findOneById('1');
203
+ assert.ok(calls[0].url.includes('relations=author'), calls[0].url);
204
+ });
205
+
206
+ // ── create() ─────────────────────────────────────────────────────────────────
207
+ await test('create() calls POST /api/collections/:slug with body', async () => {
208
+ const c = new Chadstart('http://test');
209
+ enqueue({ id: '1', title: 'Hello' });
210
+ await c.from('posts').create({ title: 'Hello' });
211
+ assert.equal(calls[0].url, 'http://test/api/collections/posts');
212
+ assert.equal(calls[0].options.method, 'POST');
213
+ assert.deepEqual(JSON.parse(calls[0].options.body), { title: 'Hello' });
214
+ });
215
+
216
+ // ── update() ─────────────────────────────────────────────────────────────────
217
+ await test('update() calls PUT /api/collections/:slug/:id', async () => {
218
+ const c = new Chadstart('http://test');
219
+ enqueue({ id: '1', title: 'World' });
220
+ await c.from('posts').update('1', { title: 'World' });
221
+ assert.equal(calls[0].url, 'http://test/api/collections/posts/1');
222
+ assert.equal(calls[0].options.method, 'PUT');
223
+ });
224
+
225
+ // ── patch() ──────────────────────────────────────────────────────────────────
226
+ await test('patch() calls PATCH /api/collections/:slug/:id', async () => {
227
+ const c = new Chadstart('http://test');
228
+ enqueue({ id: '1', title: 'World' });
229
+ await c.from('posts').patch('1', { title: 'World' });
230
+ assert.equal(calls[0].url, 'http://test/api/collections/posts/1');
231
+ assert.equal(calls[0].options.method, 'PATCH');
232
+ });
233
+
234
+ // ── delete() ─────────────────────────────────────────────────────────────────
235
+ await test('delete() calls DELETE /api/collections/:slug/:id', async () => {
236
+ const c = new Chadstart('http://test');
237
+ enqueue({ id: '1' });
238
+ await c.from('posts').delete('1');
239
+ assert.equal(calls[0].url, 'http://test/api/collections/posts/1');
240
+ assert.equal(calls[0].options.method, 'DELETE');
241
+ });
242
+
243
+ // ── Singles ───────────────────────────────────────────────────────────────────
244
+ await test('single().get() calls GET /api/singles/:slug', async () => {
245
+ const c = new Chadstart('http://test');
246
+ enqueue({ title: 'Welcome' });
247
+ await c.single('homepage').get();
248
+ assert.equal(calls[0].url, 'http://test/api/singles/homepage');
249
+ assert.equal(calls[0].options.method, 'GET');
250
+ });
251
+
252
+ await test('single().update() calls PUT /api/singles/:slug', async () => {
253
+ const c = new Chadstart('http://test');
254
+ enqueue({ title: 'New title' });
255
+ await c.single('homepage').update({ title: 'New title' });
256
+ assert.equal(calls[0].options.method, 'PUT');
257
+ assert.ok(calls[0].url.includes('/api/singles/homepage'));
258
+ });
259
+
260
+ await test('single().patch() calls PATCH /api/singles/:slug', async () => {
261
+ const c = new Chadstart('http://test');
262
+ enqueue({ title: 'New title' });
263
+ await c.single('homepage').patch({ title: 'New title' });
264
+ assert.equal(calls[0].options.method, 'PATCH');
265
+ });
266
+
267
+ // ── Auth ─────────────────────────────────────────────────────────────────────
268
+ await test('auth().signup() calls POST /api/auth/:slug/signup', async () => {
269
+ const c = new Chadstart('http://test');
270
+ enqueue({ token: 'tok123', user: { id: '1', email: 'a@b.com' } });
271
+ const result = await c.auth('customers').signup({ email: 'a@b.com', password: 'pass' });
272
+ assert.equal(calls[0].url, 'http://test/api/auth/customers/signup');
273
+ assert.equal(calls[0].options.method, 'POST');
274
+ assert.equal(result.token, 'tok123');
275
+ // Token should be stored automatically
276
+ assert.equal(c._token, 'tok123');
277
+ });
278
+
279
+ await test('auth().login() calls POST /api/auth/:slug/login', async () => {
280
+ const c = new Chadstart('http://test');
281
+ enqueue({ token: 'tok456', user: { id: '2', email: 'x@y.com' } });
282
+ const result = await c.auth('customers').login({ email: 'x@y.com', password: 'pass' });
283
+ assert.equal(calls[0].url, 'http://test/api/auth/customers/login');
284
+ assert.equal(result.token, 'tok456');
285
+ assert.equal(c._token, 'tok456');
286
+ });
287
+
288
+ await test('auth().me() calls GET /api/auth/:slug/me', async () => {
289
+ const c = new Chadstart('http://test');
290
+ c.setToken('tok789');
291
+ enqueue({ id: '1', email: 'a@b.com' });
292
+ await c.auth('customers').me();
293
+ assert.equal(calls[0].url, 'http://test/api/auth/customers/me');
294
+ assert.equal(calls[0].options.method, 'GET');
295
+ assert.ok(calls[0].options.headers['Authorization'] === 'Bearer tok789');
296
+ });
297
+
298
+ await test('auth().logout() clears the token', () => {
299
+ const c = new Chadstart('http://test');
300
+ c.setToken('tok');
301
+ c.auth('customers').logout();
302
+ assert.equal(c._token, null);
303
+ });
304
+
305
+ // ── Authorization header ──────────────────────────────────────────────────────
306
+ await test('requests include Authorization header when token is set', async () => {
307
+ const c = new Chadstart('http://test');
308
+ c.setToken('mytoken');
309
+ enqueue({ data: [] });
310
+ await c.from('posts').find();
311
+ assert.equal(calls[0].options.headers['Authorization'], 'Bearer mytoken');
312
+ });
313
+
314
+ await test('requests omit Authorization header when no token', async () => {
315
+ const c = new Chadstart('http://test');
316
+ enqueue({ data: [] });
317
+ await c.from('posts').find();
318
+ assert.equal(calls[0].options.headers['Authorization'], undefined);
319
+ });
320
+
321
+ // ── Error handling ────────────────────────────────────────────────────────────
322
+ await test('throws ChadstartError on non-2xx response', async () => {
323
+ const c = new Chadstart('http://test');
324
+ enqueue({ error: 'Not found' }, false, 404);
325
+ try {
326
+ await c.from('posts').findOneById('missing');
327
+ assert.fail('Expected error');
328
+ } catch (err) {
329
+ assert.ok(err instanceof ChadstartError);
330
+ assert.equal(err.status, 404);
331
+ assert.equal(err.message, 'Not found');
332
+ }
333
+ });
334
+
335
+ // ── Summary ───────────────────────────────────────────────────────────────────
336
+ console.log(`\n${passed} passed, ${failed} failed\n`);
337
+ if (failed > 0) process.exit(1);
338
+ }
339
+
340
+ run().catch((err) => { console.error(err); process.exit(1); });
@@ -0,0 +1,217 @@
1
+ /**
2
+ * TypeScript declarations for @chadstart/sdk
3
+ */
4
+
5
+ /** Paginated list response returned by collection find(). */
6
+ export interface PaginatedResult<T = Record<string, unknown>> {
7
+ data: T[];
8
+ currentPage: number;
9
+ lastPage: number;
10
+ from: number;
11
+ to: number;
12
+ total: number;
13
+ perPage: number;
14
+ }
15
+
16
+ /** Options for find() pagination. */
17
+ export interface FindOptions {
18
+ page?: number;
19
+ perPage?: number;
20
+ }
21
+
22
+ /** Options for orderBy(). */
23
+ export interface OrderByOptions {
24
+ desc?: boolean;
25
+ }
26
+
27
+ /** Auth signup/login response. */
28
+ export interface AuthResponse<U = Record<string, unknown>> {
29
+ token: string;
30
+ user: U;
31
+ }
32
+
33
+ /**
34
+ * Error thrown when the Chadstart API returns a non-2xx status.
35
+ */
36
+ export declare class ChadstartError extends Error {
37
+ name: 'ChadstartError';
38
+ /** HTTP status code */
39
+ status: number;
40
+ /** Raw response body */
41
+ data: unknown;
42
+ constructor(message: string, status: number, data: unknown);
43
+ }
44
+
45
+ /**
46
+ * Fluent query builder for collection CRUD operations.
47
+ */
48
+ export declare class CollectionQuery<T = Record<string, unknown>> {
49
+ /**
50
+ * Add a filter condition.
51
+ * Supports operators: =, !=, >, >=, <, <=, like, in
52
+ *
53
+ * @example
54
+ * client.from('posts').where('published = true').find()
55
+ * client.from('cats').where('age >= 2').andWhere('breed = siamese').find()
56
+ */
57
+ where(condition: string): this;
58
+
59
+ /** Alias for chaining additional filters. */
60
+ andWhere(condition: string): this;
61
+
62
+ /**
63
+ * Eagerly load relations.
64
+ * @example
65
+ * client.from('posts').with(['author', 'tags']).find()
66
+ */
67
+ with(relations: string[]): this;
68
+
69
+ /**
70
+ * Order results by a field.
71
+ * @example
72
+ * client.from('posts').orderBy('createdAt', { desc: true }).find()
73
+ */
74
+ orderBy(field: string, options?: OrderByOptions): this;
75
+
76
+ /**
77
+ * Fetch a paginated list of items.
78
+ */
79
+ find(params?: FindOptions): Promise<PaginatedResult<T>>;
80
+
81
+ /**
82
+ * Fetch a single item by its ID.
83
+ */
84
+ findOneById(id: string): Promise<T>;
85
+
86
+ /**
87
+ * Create a new item.
88
+ */
89
+ create(data: Partial<T> | Record<string, unknown>): Promise<T>;
90
+
91
+ /**
92
+ * Fully replace an item (PUT). Missing properties are set to null.
93
+ */
94
+ update(id: string, data: Partial<T> | Record<string, unknown>): Promise<T>;
95
+
96
+ /**
97
+ * Partially update an item (PATCH). Only provided properties are changed.
98
+ */
99
+ patch(id: string, data: Partial<T> | Record<string, unknown>): Promise<T>;
100
+
101
+ /**
102
+ * Permanently delete an item.
103
+ */
104
+ delete(id: string): Promise<T>;
105
+ }
106
+
107
+ /**
108
+ * Fluent query builder for single entity operations.
109
+ */
110
+ export declare class SingleQuery<T = Record<string, unknown>> {
111
+ /**
112
+ * Fetch the single entity.
113
+ */
114
+ get(): Promise<T>;
115
+
116
+ /**
117
+ * Fully replace the single entity (PUT).
118
+ */
119
+ update(data: Partial<T> | Record<string, unknown>): Promise<T>;
120
+
121
+ /**
122
+ * Partially update the single entity (PATCH).
123
+ */
124
+ patch(data: Partial<T> | Record<string, unknown>): Promise<T>;
125
+ }
126
+
127
+ /**
128
+ * Fluent query builder for authentication operations.
129
+ */
130
+ export declare class AuthQuery<U = Record<string, unknown>> {
131
+ /**
132
+ * Register a new user. Automatically stores the returned token.
133
+ */
134
+ signup(data: { email: string; password: string; [key: string]: unknown }): Promise<AuthResponse<U>>;
135
+
136
+ /**
137
+ * Log in an existing user. Automatically stores the returned token.
138
+ */
139
+ login(data: { email: string; password: string }): Promise<AuthResponse<U>>;
140
+
141
+ /**
142
+ * Get the currently authenticated user.
143
+ * Requires a token to be set via setToken() or a previous signup/login.
144
+ */
145
+ me(): Promise<U>;
146
+
147
+ /**
148
+ * Clear the stored token (log out).
149
+ */
150
+ logout(): void;
151
+ }
152
+
153
+ /**
154
+ * Chadstart SDK client.
155
+ *
156
+ * @example
157
+ * import Chadstart from '@chadstart/sdk'
158
+ *
159
+ * const client = new Chadstart('http://localhost:3000')
160
+ *
161
+ * // Collections
162
+ * const { data } = await client.from('posts').find()
163
+ * const post = await client.from('posts').findOneById(id)
164
+ * const post = await client.from('posts').create({ title: 'Hello' })
165
+ * const post = await client.from('posts').update(id, { title: 'World' })
166
+ * const post = await client.from('posts').patch(id, { title: 'World' })
167
+ * const deleted = await client.from('posts').delete(id)
168
+ *
169
+ * // Filtering, ordering & relations
170
+ * const posts = await client
171
+ * .from('posts')
172
+ * .where('published = true')
173
+ * .orderBy('createdAt', { desc: true })
174
+ * .with(['author'])
175
+ * .find({ page: 1, perPage: 20 })
176
+ *
177
+ * // Singles
178
+ * const page = await client.single('homepage').get()
179
+ * const page = await client.single('homepage').patch({ title: 'Welcome!' })
180
+ *
181
+ * // Auth
182
+ * const { token, user } = await client.auth('customers').login({ email, password })
183
+ * client.setToken(token)
184
+ * const me = await client.auth('customers').me()
185
+ */
186
+ export declare class Chadstart {
187
+ constructor(baseUrl?: string);
188
+
189
+ /**
190
+ * Start a collection CRUD query builder.
191
+ * @param slug - Collection slug, e.g. 'posts'
192
+ */
193
+ from<T = Record<string, unknown>>(slug: string): CollectionQuery<T>;
194
+
195
+ /**
196
+ * Start a single entity query builder.
197
+ * @param slug - Single entity slug, e.g. 'homepage'
198
+ */
199
+ single<T = Record<string, unknown>>(slug: string): SingleQuery<T>;
200
+
201
+ /**
202
+ * Start an authentication query builder.
203
+ * @param slug - Authenticable entity slug, e.g. 'customers'
204
+ */
205
+ auth<U = Record<string, unknown>>(slug: string): AuthQuery<U>;
206
+
207
+ /**
208
+ * Set the Bearer token used for authenticated requests.
209
+ * Called automatically after a successful signup() or login().
210
+ */
211
+ setToken(token: string): void;
212
+
213
+ /** Clear the stored token. */
214
+ clearToken(): void;
215
+ }
216
+
217
+ export default Chadstart;