@hatk/hatk 0.0.1-alpha.6 → 0.0.1-alpha.61

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 (163) hide show
  1. package/dist/adapter.d.ts +19 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +108 -0
  4. package/dist/backfill.d.ts +2 -2
  5. package/dist/backfill.d.ts.map +1 -1
  6. package/dist/backfill.js +83 -41
  7. package/dist/car.d.ts +42 -10
  8. package/dist/car.d.ts.map +1 -1
  9. package/dist/car.js +154 -14
  10. package/dist/cli.js +243 -1043
  11. package/dist/config.d.ts +31 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +40 -9
  14. package/dist/database/adapter-factory.d.ts +6 -0
  15. package/dist/database/adapter-factory.d.ts.map +1 -0
  16. package/dist/database/adapter-factory.js +20 -0
  17. package/dist/database/adapters/duckdb-search.d.ts +12 -0
  18. package/dist/database/adapters/duckdb-search.d.ts.map +1 -0
  19. package/dist/database/adapters/duckdb-search.js +27 -0
  20. package/dist/database/adapters/duckdb.d.ts +25 -0
  21. package/dist/database/adapters/duckdb.d.ts.map +1 -0
  22. package/dist/database/adapters/duckdb.js +161 -0
  23. package/dist/database/adapters/sqlite-search.d.ts +23 -0
  24. package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
  25. package/dist/database/adapters/sqlite-search.js +74 -0
  26. package/dist/database/adapters/sqlite.d.ts +18 -0
  27. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  28. package/dist/database/adapters/sqlite.js +88 -0
  29. package/dist/{db.d.ts → database/db.d.ts} +57 -6
  30. package/dist/database/db.d.ts.map +1 -0
  31. package/dist/{db.js → database/db.js} +730 -549
  32. package/dist/database/dialect.d.ts +45 -0
  33. package/dist/database/dialect.d.ts.map +1 -0
  34. package/dist/database/dialect.js +72 -0
  35. package/dist/{fts.d.ts → database/fts.d.ts} +7 -0
  36. package/dist/database/fts.d.ts.map +1 -0
  37. package/dist/{fts.js → database/fts.js} +116 -32
  38. package/dist/database/index.d.ts +7 -0
  39. package/dist/database/index.d.ts.map +1 -0
  40. package/dist/database/index.js +6 -0
  41. package/dist/database/ports.d.ts +50 -0
  42. package/dist/database/ports.d.ts.map +1 -0
  43. package/dist/database/ports.js +1 -0
  44. package/dist/{schema.d.ts → database/schema.d.ts} +14 -3
  45. package/dist/database/schema.d.ts.map +1 -0
  46. package/dist/{schema.js → database/schema.js} +81 -41
  47. package/dist/dev-entry.d.ts +8 -0
  48. package/dist/dev-entry.d.ts.map +1 -0
  49. package/dist/dev-entry.js +113 -0
  50. package/dist/feeds.d.ts +12 -8
  51. package/dist/feeds.d.ts.map +1 -1
  52. package/dist/feeds.js +51 -6
  53. package/dist/hooks.d.ts +85 -0
  54. package/dist/hooks.d.ts.map +1 -0
  55. package/dist/hooks.js +161 -0
  56. package/dist/hydrate.d.ts +7 -6
  57. package/dist/hydrate.d.ts.map +1 -1
  58. package/dist/hydrate.js +4 -16
  59. package/dist/indexer.d.ts +23 -0
  60. package/dist/indexer.d.ts.map +1 -1
  61. package/dist/indexer.js +181 -34
  62. package/dist/labels.d.ts +36 -0
  63. package/dist/labels.d.ts.map +1 -1
  64. package/dist/labels.js +71 -6
  65. package/dist/lexicon-resolve.d.ts.map +1 -1
  66. package/dist/lexicon-resolve.js +27 -112
  67. package/dist/lexicons/com/atproto/label/defs.json +75 -0
  68. package/dist/lexicons/com/atproto/moderation/defs.json +30 -0
  69. package/dist/lexicons/com/atproto/repo/strongRef.json +24 -0
  70. package/dist/lexicons/dev/hatk/applyWrites.json +87 -0
  71. package/dist/lexicons/dev/hatk/createRecord.json +40 -0
  72. package/dist/lexicons/dev/hatk/createReport.json +48 -0
  73. package/dist/lexicons/dev/hatk/deleteRecord.json +25 -0
  74. package/dist/lexicons/dev/hatk/describeCollections.json +41 -0
  75. package/dist/lexicons/dev/hatk/describeFeeds.json +29 -0
  76. package/dist/lexicons/dev/hatk/describeLabels.json +45 -0
  77. package/dist/lexicons/dev/hatk/getFeed.json +30 -0
  78. package/dist/lexicons/dev/hatk/getPreferences.json +19 -0
  79. package/dist/lexicons/dev/hatk/getRecord.json +26 -0
  80. package/dist/lexicons/dev/hatk/getRecords.json +32 -0
  81. package/dist/lexicons/dev/hatk/putPreference.json +28 -0
  82. package/dist/lexicons/dev/hatk/putRecord.json +41 -0
  83. package/dist/lexicons/dev/hatk/searchRecords.json +32 -0
  84. package/dist/lexicons/dev/hatk/uploadBlob.json +23 -0
  85. package/dist/logger.d.ts +29 -0
  86. package/dist/logger.d.ts.map +1 -1
  87. package/dist/logger.js +29 -0
  88. package/dist/main.js +138 -67
  89. package/dist/mst.d.ts +18 -1
  90. package/dist/mst.d.ts.map +1 -1
  91. package/dist/mst.js +19 -8
  92. package/dist/oauth/db.d.ts +3 -1
  93. package/dist/oauth/db.d.ts.map +1 -1
  94. package/dist/oauth/db.js +48 -19
  95. package/dist/oauth/server.d.ts +24 -0
  96. package/dist/oauth/server.d.ts.map +1 -1
  97. package/dist/oauth/server.js +198 -22
  98. package/dist/oauth/session.d.ts +11 -0
  99. package/dist/oauth/session.d.ts.map +1 -0
  100. package/dist/oauth/session.js +65 -0
  101. package/dist/opengraph.d.ts +10 -0
  102. package/dist/opengraph.d.ts.map +1 -1
  103. package/dist/opengraph.js +80 -40
  104. package/dist/pds-proxy.d.ts +60 -0
  105. package/dist/pds-proxy.d.ts.map +1 -0
  106. package/dist/pds-proxy.js +277 -0
  107. package/dist/push.d.ts +34 -0
  108. package/dist/push.d.ts.map +1 -0
  109. package/dist/push.js +184 -0
  110. package/dist/renderer.d.ts +27 -0
  111. package/dist/renderer.d.ts.map +1 -0
  112. package/dist/renderer.js +46 -0
  113. package/dist/resolve-hatk.d.ts +6 -0
  114. package/dist/resolve-hatk.d.ts.map +1 -0
  115. package/dist/resolve-hatk.js +20 -0
  116. package/dist/response.d.ts +16 -0
  117. package/dist/response.d.ts.map +1 -0
  118. package/dist/response.js +69 -0
  119. package/dist/scanner.d.ts +21 -0
  120. package/dist/scanner.d.ts.map +1 -0
  121. package/dist/scanner.js +88 -0
  122. package/dist/seed.d.ts +19 -0
  123. package/dist/seed.d.ts.map +1 -1
  124. package/dist/seed.js +43 -4
  125. package/dist/server-init.d.ts +8 -0
  126. package/dist/server-init.d.ts.map +1 -0
  127. package/dist/server-init.js +62 -0
  128. package/dist/server.d.ts +26 -3
  129. package/dist/server.d.ts.map +1 -1
  130. package/dist/server.js +629 -635
  131. package/dist/setup.d.ts +28 -1
  132. package/dist/setup.d.ts.map +1 -1
  133. package/dist/setup.js +50 -3
  134. package/dist/templates/feed.tpl +14 -0
  135. package/dist/templates/hook.tpl +5 -0
  136. package/dist/templates/label.tpl +15 -0
  137. package/dist/templates/og.tpl +17 -0
  138. package/dist/templates/seed.tpl +11 -0
  139. package/dist/templates/setup.tpl +5 -0
  140. package/dist/templates/test-feed.tpl +19 -0
  141. package/dist/templates/test-xrpc.tpl +19 -0
  142. package/dist/templates/xrpc.tpl +41 -0
  143. package/dist/test.d.ts +1 -1
  144. package/dist/test.d.ts.map +1 -1
  145. package/dist/test.js +39 -32
  146. package/dist/views.js +1 -1
  147. package/dist/vite-plugin.d.ts +1 -1
  148. package/dist/vite-plugin.d.ts.map +1 -1
  149. package/dist/vite-plugin.js +254 -66
  150. package/dist/xrpc.d.ts +75 -11
  151. package/dist/xrpc.d.ts.map +1 -1
  152. package/dist/xrpc.js +189 -39
  153. package/package.json +14 -7
  154. package/public/admin.html +133 -54
  155. package/dist/db.d.ts.map +0 -1
  156. package/dist/fts.d.ts.map +0 -1
  157. package/dist/oauth/hooks.d.ts +0 -10
  158. package/dist/oauth/hooks.d.ts.map +0 -1
  159. package/dist/oauth/hooks.js +0 -40
  160. package/dist/schema.d.ts.map +0 -1
  161. package/dist/test-browser.d.ts +0 -14
  162. package/dist/test-browser.d.ts.map +0 -1
  163. package/dist/test-browser.js +0 -26
@@ -1,87 +1,275 @@
1
- import { spawn } from 'node:child_process';
1
+ import { createRunnableDevEnvironment } from 'vite';
2
2
  import { resolve } from 'node:path';
3
3
  import { existsSync } from 'node:fs';
4
+ import { execSync } from 'node:child_process';
5
+ import { isHatkRoute } from "./adapter.js";
6
+ /** Boot the local PDS if a docker-compose.yml exists. */
7
+ async function ensurePds() {
8
+ if (!existsSync(resolve('docker-compose.yml')))
9
+ return;
10
+ try {
11
+ const res = await fetch('http://localhost:2583/xrpc/_health');
12
+ if (res.ok)
13
+ return;
14
+ }
15
+ catch { }
16
+ console.log('[hatk] Starting PDS...');
17
+ execSync('docker compose up -d', { stdio: 'inherit', cwd: process.cwd() });
18
+ for (let i = 0; i < 30; i++) {
19
+ try {
20
+ const res = await fetch('http://localhost:2583/xrpc/_health');
21
+ if (res.ok) {
22
+ console.log('[hatk] PDS ready');
23
+ return;
24
+ }
25
+ }
26
+ catch { }
27
+ await new Promise((r) => setTimeout(r, 1000));
28
+ }
29
+ console.error('[hatk] PDS failed to start');
30
+ }
31
+ /** Run seed file if it exists. */
32
+ function runSeed() {
33
+ const seedFile = resolve('seeds/seed.ts');
34
+ if (!existsSync(seedFile))
35
+ return;
36
+ try {
37
+ execSync(`npx tsx ${seedFile}`, { stdio: 'inherit', cwd: process.cwd() });
38
+ }
39
+ catch { }
40
+ }
41
+ /** Walk all loaded modules in the module graphs to collect CSS URLs for SSR. */
42
+ function collectAllCss(server) {
43
+ const cssUrls = new Set();
44
+ for (const envName of ['hatk', 'client']) {
45
+ const env = server.environments[envName];
46
+ if (!env?.moduleGraph)
47
+ continue;
48
+ // TODO: uses internal Vite module graph API — may break across Vite minor versions
49
+ for (const mod of env.moduleGraph.idToModuleMap?.values?.() ?? []) {
50
+ const url = mod.url || '';
51
+ if (/\.(css|scss|less|styl|stylus|pcss|postcss)(\?|$)/.test(url)) {
52
+ cssUrls.add(url);
53
+ }
54
+ if (url.includes('type=style')) {
55
+ cssUrls.add(url);
56
+ }
57
+ }
58
+ }
59
+ if (cssUrls.size === 0)
60
+ return '';
61
+ return Array.from(cssUrls)
62
+ .map((url) => `<link rel="stylesheet" href="${url}">`)
63
+ .join('\n');
64
+ }
4
65
  export function hatk(opts) {
5
- const devPort = 3000;
6
- const backendPort = opts?.port ?? devPort + 1;
7
- const issuer = `http://127.0.0.1:${devPort}`;
8
- let serverProcess = null;
66
+ const devPort = opts?.port ?? 3000;
67
+ let handler = null;
68
+ let ssrRenderPage = null;
69
+ let ssrGetRenderer = null;
70
+ let reloadServer = null;
71
+ let reloadTimer = null;
9
72
  return {
10
73
  name: 'vite-plugin-hatk',
74
+ // Rewrite $hatk imports in source code so SSR module runners can resolve them.
75
+ // vite-plus's fetchModule bypasses resolve.alias for bare imports.
76
+ transform(code, _id) {
77
+ if (!code.includes('$hatk'))
78
+ return;
79
+ const hatk = resolve('hatk.generated.ts');
80
+ const hatkClient = resolve('hatk.generated.client.ts');
81
+ return code
82
+ .replace(/from\s+['"](\$hatk\/client)['"]/g, `from '${hatkClient}'`)
83
+ .replace(/from\s+['"](\$hatk)['"]/g, `from '${hatk}'`);
84
+ },
11
85
  config() {
12
- const target = `http://127.0.0.1:${backendPort}`;
13
- // changeOrigin: false preserves the original Host header so DPoP htu matches
14
- const rule = { target, changeOrigin: false };
15
86
  return {
16
- server: {
17
- host: '127.0.0.1',
18
- port: devPort,
19
- proxy: {
20
- '/xrpc': rule,
21
- '/oauth/par': rule,
22
- '/oauth/token': rule,
23
- '/oauth/jwks': rule,
24
- '/oauth/authorize': rule,
25
- '/oauth/callback': {
26
- ...rule,
27
- // Only proxy the PDS callback (iss !== our issuer) to the backend.
28
- // The client-side callback (iss === our issuer) should reach the SPA.
29
- bypass(req) {
30
- const url = new URL(req.url, issuer);
31
- if (url.searchParams.get('iss') === issuer)
32
- return req.url;
33
- },
34
- },
35
- '/oauth/client-metadata.json': rule,
36
- '/oauth-client-metadata.json': rule,
37
- '/.well-known': rule,
38
- '/info': rule,
39
- '/repos': rule,
40
- '/og': rule,
41
- '/admin': rule,
42
- '/_health': rule,
87
+ resolve: {
88
+ alias: {
89
+ '$hatk/client': resolve('hatk.generated.client.ts'),
90
+ $hatk: resolve('hatk.generated.ts'),
43
91
  },
44
92
  },
45
- test: {
46
- projects: [
47
- {
48
- test: {
49
- name: 'unit',
50
- include: ['test/feeds/**/*.test.ts', 'test/xrpc/**/*.test.ts'],
93
+ environments: {
94
+ hatk: {
95
+ resolve: {
96
+ conditions: ['svelte'],
97
+ noExternal: ['svelte', '@tanstack/svelte-query'],
98
+ external: true,
99
+ },
100
+ dev: {
101
+ createEnvironment(name, config) {
102
+ return createRunnableDevEnvironment(name, config);
103
+ },
104
+ optimizeDeps: {
105
+ exclude: ['better-sqlite3', '@duckdb/node-api'],
51
106
  },
52
107
  },
53
- {
54
- test: {
55
- name: 'integration',
56
- include: ['test/integration/**/*.test.ts'],
108
+ build: {
109
+ outDir: 'dist/server',
110
+ ssr: true,
111
+ rollupOptions: {
112
+ external: ['better-sqlite3', '@duckdb/node-api'],
57
113
  },
58
114
  },
59
- ],
115
+ },
116
+ },
117
+ server: {
118
+ host: '127.0.0.1',
119
+ port: devPort,
120
+ fs: {
121
+ allow: ['.'],
122
+ },
123
+ watch: {
124
+ ignored: ['**/db/**', '**/data/**'],
125
+ },
60
126
  },
61
127
  };
62
128
  },
63
- configureServer(server) {
64
- const mainPath = resolve(import.meta.dirname, 'main.js');
65
- const watchDirs = ['xrpc', 'feeds', 'labels', 'jobs', 'setup', 'lexicons'].filter((d) => existsSync(d));
66
- const watchArgs = watchDirs.flatMap((d) => ['--watch-path', d]);
67
- serverProcess = spawn('npx', ['tsx', 'watch', ...watchArgs, mainPath, 'config.yaml'], {
68
- stdio: 'inherit',
69
- cwd: process.cwd(),
70
- env: {
71
- ...process.env,
72
- PORT: String(backendPort),
73
- OAUTH_ISSUER: process.env.OAUTH_ISSUER || issuer,
74
- DEV_MODE: '1',
75
- },
76
- });
77
- server.httpServer?.on('close', () => {
78
- serverProcess?.kill();
79
- serverProcess = null;
129
+ async configureServer(server) {
130
+ // Skip hatk server boot in test mode — tests manage their own context
131
+ if (process.env.VITEST)
132
+ return;
133
+ // Boot PDS and run seeds before starting
134
+ await ensurePds();
135
+ runSeed();
136
+ const env = server.environments.hatk;
137
+ if (!env || !('runner' in env)) {
138
+ console.error('[hatk] hatk environment not available — is Vite 8 with Environment API?');
139
+ return;
140
+ }
141
+ // Load the hatk boot module through the module runner
142
+ const mainPath = resolve(import.meta.dirname, 'dev-entry.js');
143
+ const mod = await env.runner.import(mainPath);
144
+ handler = mod.handler;
145
+ ssrRenderPage = mod.renderPage;
146
+ ssrGetRenderer = mod.getRenderer;
147
+ reloadServer = mod.reloadServer;
148
+ globalThis.__hatk_callXrpc = mod.callXrpc;
149
+ // Capture cookie parser and name for SSR viewer resolution
150
+ const ssrParseSessionCookie = mod.parseSessionCookie ?? null;
151
+ globalThis.__hatk_parseSessionCookie = ssrParseSessionCookie;
152
+ globalThis.__hatk_sessionCookieName = mod.getSessionCookieName?.() ?? '__hatk_session';
153
+ if (ssrGetRenderer?.()) {
154
+ console.log('[hatk] SSR ready');
155
+ }
156
+ // API routes — must run before Vite's static middleware
157
+ server.middlewares.use(async (req, res, next) => {
158
+ const url = new URL(req.url, `http://localhost:${devPort}`);
159
+ if (!isHatkRoute(url.pathname) || !handler) {
160
+ next();
161
+ return;
162
+ }
163
+ try {
164
+ const { toRequest, sendResponse } = await import('./adapter.js');
165
+ const request = toRequest(req, `http://localhost:${devPort}`);
166
+ const response = await handler(request);
167
+ if (response.status === 404) {
168
+ next();
169
+ return;
170
+ }
171
+ await sendResponse(res, response);
172
+ }
173
+ catch (err) {
174
+ console.error('[hatk]', err.message);
175
+ next(err);
176
+ }
80
177
  });
178
+ // SSR middleware — returned function runs after htmlFallback but before indexHtmlMiddleware
179
+ return () => {
180
+ server.middlewares.use(async (req, res, next) => {
181
+ if (!ssrGetRenderer?.()) {
182
+ next();
183
+ return;
184
+ }
185
+ const accept = req.headers.accept || '';
186
+ const url = req.originalUrl || req.url;
187
+ if (!accept.includes('text/html') || !url) {
188
+ next();
189
+ return;
190
+ }
191
+ try {
192
+ const { readFileSync } = await import('node:fs');
193
+ const rawHtml = readFileSync(resolve('index.html'), 'utf-8');
194
+ const template = await server.transformIndexHtml(url, rawHtml);
195
+ const fullUrl = new URL(url, `http://localhost:${devPort}`);
196
+ const headers = {};
197
+ if (req.headers.cookie)
198
+ headers.cookie = req.headers.cookie;
199
+ const request = new Request(fullUrl.href, { headers });
200
+ // Resolve viewer from session cookie for SSR
201
+ // TODO: globalThis.__hatk_viewer is not safe for concurrent SSR requests.
202
+ // Replace with AsyncLocalStorage when callXrpc supports per-request context.
203
+ let viewer = null;
204
+ if (ssrParseSessionCookie) {
205
+ try {
206
+ viewer = await ssrParseSessionCookie(request);
207
+ }
208
+ catch { }
209
+ }
210
+ ;
211
+ globalThis.__hatk_viewer = viewer;
212
+ let renderedHtml;
213
+ try {
214
+ renderedHtml = await ssrRenderPage(template, request);
215
+ }
216
+ finally {
217
+ ;
218
+ globalThis.__hatk_viewer = null;
219
+ }
220
+ // Inject viewer into HTML so client has it before OAuth initializes
221
+ if (renderedHtml && viewer) {
222
+ const script = `<script>globalThis.__hatk_viewer=${JSON.stringify(viewer)}</script>`;
223
+ renderedHtml = renderedHtml.replace('</head>', `${script}\n</head>`);
224
+ }
225
+ if (!renderedHtml) {
226
+ next();
227
+ return;
228
+ }
229
+ // Collect CSS from all loaded modules to prevent FOUC
230
+ const cssLinks = collectAllCss(server);
231
+ let html = renderedHtml;
232
+ if (cssLinks) {
233
+ html = html.replace('</head>', `${cssLinks}\n</head>`);
234
+ }
235
+ res.setHeader('Content-Type', 'text/html');
236
+ res.end(html);
237
+ }
238
+ catch (err) {
239
+ console.error('[hatk] SSR error:', err.message);
240
+ next(err);
241
+ }
242
+ });
243
+ };
244
+ },
245
+ // Handle HMR for server/ files in the hatk environment
246
+ hotUpdate(options) {
247
+ if (options.file.includes('/server/') && reloadServer) {
248
+ // Debounce: hotUpdate fires once per environment, only reload once
249
+ if (!reloadTimer) {
250
+ reloadTimer = setTimeout(() => {
251
+ reloadTimer = null;
252
+ reloadServer()
253
+ .then(() => {
254
+ console.log('[hatk] Server handlers reloaded');
255
+ })
256
+ .catch((err) => {
257
+ console.error('[hatk] Failed to reload server handlers:', err.message);
258
+ });
259
+ }, 50);
260
+ }
261
+ }
81
262
  },
82
- buildEnd() {
83
- serverProcess?.kill();
84
- serverProcess = null;
263
+ // Two-stage production build
264
+ async buildApp(builder) {
265
+ // Stage 1: Build client
266
+ if (builder.environments.client) {
267
+ await builder.build(builder.environments.client);
268
+ }
269
+ // Stage 2: Build hatk server (if environment exists)
270
+ if (builder.environments.hatk) {
271
+ await builder.build(builder.environments.hatk);
272
+ }
85
273
  },
86
274
  };
87
275
  }
package/dist/xrpc.d.ts CHANGED
@@ -1,26 +1,38 @@
1
+ import type { BaseContext } from './hydrate.ts';
1
2
  import type { Row, FlatRow } from './lex-types.ts';
3
+ import type { OAuthConfig, CdnConfig } from './config.ts';
2
4
  export type { Row, FlatRow };
5
+ /** Set the OAuth config used for record write helpers. Called once during boot. */
6
+ export declare function configureOAuth(config: OAuthConfig | null): void;
7
+ /** Thrown from XRPC handlers to return a 400 response with an error message. */
3
8
  export declare class InvalidRequestError extends Error {
4
9
  status: number;
5
10
  errorName?: string;
6
11
  constructor(message: string, errorName?: string);
7
12
  }
13
+ /** Thrown from XRPC handlers to return a 404 response. */
8
14
  export declare class NotFoundError extends InvalidRequestError {
9
15
  status: number;
10
16
  constructor(message?: string);
11
17
  }
12
- export interface XrpcContext<P = Record<string, string>, Records extends Record<string, any> = Record<string, any>, I = unknown> {
18
+ /**
19
+ * Context passed to every XRPC handler. Provides database access, pagination
20
+ * helpers, viewer auth, record resolution, full-text search, label queries,
21
+ * and blob URL generation.
22
+ *
23
+ * @typeParam P - Query parameter types (derived from lexicon)
24
+ * @typeParam Records - Map of collection NSID → record type (from generated types)
25
+ * @typeParam I - Input body type for procedure calls
26
+ */
27
+ export interface XrpcContext<P = Record<string, string>, Records extends Record<string, any> = Record<string, any>, I = unknown> extends BaseContext {
13
28
  db: {
14
- query: (sql: string, params?: any[]) => Promise<any[]>;
15
- run: (sql: string, ...params: any[]) => Promise<void>;
29
+ query: (sql: string, params?: unknown[]) => Promise<unknown[]>;
30
+ run: (sql: string, params?: unknown[]) => Promise<void>;
16
31
  };
17
32
  params: P;
18
33
  input: I;
19
34
  cursor?: string;
20
35
  limit: number;
21
- viewer: {
22
- did: string;
23
- } | null;
24
36
  packCursor: (primary: string | number, cid: string) => string;
25
37
  unpackCursor: (cursor: string) => {
26
38
  primary: string;
@@ -37,17 +49,69 @@ export interface XrpcContext<P = Record<string, string>, Records extends Record<
37
49
  cursor?: string;
38
50
  }>;
39
51
  resolve: <R = unknown>(uris: string[]) => Promise<Row<R>[]>;
40
- lookup: <R = any>(collection: string, field: string, values: string[]) => Promise<Map<string, Row<R>>>;
41
- count: (collection: string, field: string, values: string[]) => Promise<Map<string, number>>;
42
52
  exists: (collection: string, filters: Record<string, string>) => Promise<boolean>;
43
- labels: (uris: string[]) => Promise<Map<string, any[]>>;
44
- blobUrl: (did: string, ref: unknown, preset?: 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize') => string | undefined;
53
+ createRecord: (collection: string, record: Record<string, unknown>, opts?: {
54
+ rkey?: string;
55
+ }) => Promise<{
56
+ uri?: string;
57
+ cid?: string;
58
+ }>;
59
+ putRecord: (collection: string, rkey: string, record: Record<string, unknown>) => Promise<{
60
+ uri?: string;
61
+ cid?: string;
62
+ }>;
63
+ deleteRecord: (collection: string, rkey: string) => Promise<void>;
64
+ applyWrites: (writes: Array<{
65
+ $type: string;
66
+ collection: string;
67
+ rkey?: string;
68
+ value?: Record<string, unknown>;
69
+ }>) => Promise<{
70
+ results?: Array<{
71
+ $type: string;
72
+ uri?: string;
73
+ cid?: string;
74
+ }>;
75
+ }>;
45
76
  }
77
+ /** Set the relay URL used for blob URL generation. Called once during boot. */
46
78
  export declare function configureRelay(relay: string): void;
47
- export declare function blobUrl(did: string, ref: unknown, preset?: 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize'): string | undefined;
79
+ /** Set the CDN config for imgproxy URL signing. Called once during boot. */
80
+ export declare function configureCdn(cdn: CdnConfig | null): void;
81
+ /**
82
+ * Generate a CDN URL for a blob ref. Uses the PDS directly in local dev,
83
+ * a configured imgproxy CDN if available, or the Bluesky CDN as fallback.
84
+ */
85
+ export declare function blobUrl(did: string, ref: unknown, preset?: string): string | undefined;
86
+ /** Build a full XrpcContext from request parameters. Reuses buildBaseContext for shared fields. */
87
+ export declare function buildXrpcContext(params: Record<string, string>, cursor: string | undefined, limit: number, viewer: {
88
+ did: string;
89
+ handle?: string;
90
+ } | null, input?: unknown): XrpcContext;
91
+ /**
92
+ * Discover and load XRPC handler modules from the `xrpc/` directory.
93
+ * Directory nesting maps to NSID segments. Parameters are validated and
94
+ * coerced against the matching lexicon definition.
95
+ */
48
96
  export declare function initXrpc(xrpcDir: string): Promise<void>;
97
+ /** Register a single XRPC handler from a scanned server/ module. */
98
+ export declare function registerXrpcHandler(nsid: string, handlerModule: {
99
+ handler: (ctx: any) => Promise<any>;
100
+ }): void;
101
+ /** Execute a registered XRPC handler by name. Returns null if no handler matches. */
49
102
  export declare function executeXrpc(name: string, params: Record<string, string>, cursor: string | undefined, limit: number, viewer?: {
50
103
  did: string;
51
104
  } | null, input?: unknown): Promise<any | null>;
105
+ /** Call a registered XRPC handler directly (no HTTP). For use in SSR renderers. */
106
+ export declare function callXrpc(nsid: string, params?: Record<string, any>, input?: unknown): Promise<any>;
107
+ /**
108
+ * Register a core XRPC handler directly (no XrpcContext wrapping).
109
+ * Used for built-in dev.hatk.* handlers that manage their own dependencies.
110
+ */
111
+ export declare function registerCoreXrpcHandler(nsid: string, fn: (params: Record<string, string>, cursor: string | undefined, limit: number, viewer: {
112
+ did: string;
113
+ handle?: string;
114
+ } | null, input?: unknown) => Promise<any>): void;
115
+ /** Return all registered XRPC method names. */
52
116
  export declare function listXrpc(): string[];
53
117
  //# sourceMappingURL=xrpc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}