@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
@@ -0,0 +1,69 @@
1
+ import { gzipSync } from 'node:zlib';
2
+ import { normalizeValue } from "./database/db.js";
3
+ /**
4
+ * Create a JSON Response with optional gzip compression.
5
+ * Mirrors the old jsonResponse/sendJson behavior.
6
+ */
7
+ export function json(data, status = 200, acceptEncoding) {
8
+ const body = Buffer.from(JSON.stringify(data, (_, v) => normalizeValue(v)));
9
+ if (body.length > 1024 && acceptEncoding && /\bgzip\b/.test(acceptEncoding)) {
10
+ const compressed = gzipSync(body);
11
+ return new Response(compressed, {
12
+ status,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'Content-Encoding': 'gzip',
16
+ Vary: 'Accept-Encoding',
17
+ ...(status === 200 ? { 'Cache-Control': 'no-store' } : {}),
18
+ },
19
+ });
20
+ }
21
+ return new Response(body, {
22
+ status,
23
+ headers: {
24
+ 'Content-Type': 'application/json',
25
+ ...(status === 200 ? { 'Cache-Control': 'no-store' } : {}),
26
+ },
27
+ });
28
+ }
29
+ /** Create a JSON error Response. */
30
+ export function jsonError(status, message, acceptEncoding) {
31
+ return json({ error: message }, status, acceptEncoding);
32
+ }
33
+ /** CORS preflight Response. */
34
+ export function cors() {
35
+ return new Response(null, {
36
+ status: 200,
37
+ headers: {
38
+ 'Access-Control-Allow-Origin': '*',
39
+ 'Access-Control-Allow-Headers': '*',
40
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
41
+ },
42
+ });
43
+ }
44
+ /** Add CORS headers to an existing Response. */
45
+ export function withCors(response) {
46
+ const headers = new Headers(response.headers);
47
+ headers.set('Access-Control-Allow-Origin', '*');
48
+ headers.set('Access-Control-Allow-Headers', '*');
49
+ headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
50
+ return new Response(response.body, {
51
+ status: response.status,
52
+ statusText: response.statusText,
53
+ headers,
54
+ });
55
+ }
56
+ /** Create a static file Response with correct MIME type. */
57
+ export function file(content, contentType, cacheControl) {
58
+ return new Response(Buffer.from(content), {
59
+ status: 200,
60
+ headers: {
61
+ 'Content-Type': contentType,
62
+ ...(cacheControl ? { 'Cache-Control': cacheControl } : {}),
63
+ },
64
+ });
65
+ }
66
+ /** 404 Not Found. */
67
+ export function notFound() {
68
+ return new Response('Not Found', { status: 404 });
69
+ }
@@ -0,0 +1,21 @@
1
+ export interface ScannedModule {
2
+ path: string;
3
+ name: string;
4
+ mod: any;
5
+ }
6
+ export interface ScanResult {
7
+ feeds: ScannedModule[];
8
+ queries: ScannedModule[];
9
+ procedures: ScannedModule[];
10
+ hooks: ScannedModule[];
11
+ setup: ScannedModule[];
12
+ labels: ScannedModule[];
13
+ og: ScannedModule[];
14
+ renderer: ScannedModule | null;
15
+ }
16
+ /**
17
+ * Scan a directory for hatk server modules.
18
+ * Each file's default export is inspected for a `__type` tag.
19
+ */
20
+ export declare function scanServerDir(serverDir: string): Promise<ScanResult>;
21
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;CACT;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,aAAa,EAAE,CAAA;IACtB,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAA;IACtB,KAAK,EAAE,aAAa,EAAE,CAAA;IACtB,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,EAAE,EAAE,aAAa,EAAE,CAAA;IACnB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAA;CAC/B;AAmBD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA2D1E"}
@@ -0,0 +1,88 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { resolve, relative } from 'node:path';
10
+ import { readdirSync, statSync, existsSync } from 'node:fs';
11
+ import { log } from "./logger.js";
12
+ /** Recursively collect .ts/.js files, skipping _ prefixed and dot files */
13
+ function walkDir(dir) {
14
+ const results = [];
15
+ try {
16
+ for (const entry of readdirSync(dir)) {
17
+ if (entry.startsWith('_') || entry.startsWith('.'))
18
+ continue;
19
+ const full = resolve(dir, entry);
20
+ if (statSync(full).isDirectory()) {
21
+ results.push(...walkDir(full));
22
+ }
23
+ else if (entry.endsWith('.ts') || entry.endsWith('.js')) {
24
+ results.push(full);
25
+ }
26
+ }
27
+ }
28
+ catch { }
29
+ return results.sort();
30
+ }
31
+ /**
32
+ * Scan a directory for hatk server modules.
33
+ * Each file's default export is inspected for a `__type` tag.
34
+ */
35
+ export async function scanServerDir(serverDir) {
36
+ const result = {
37
+ feeds: [],
38
+ queries: [],
39
+ procedures: [],
40
+ hooks: [],
41
+ setup: [],
42
+ labels: [],
43
+ og: [],
44
+ renderer: null,
45
+ };
46
+ if (!existsSync(serverDir))
47
+ return result;
48
+ const files = walkDir(serverDir);
49
+ for (const filePath of files) {
50
+ const name = relative(serverDir, filePath).replace(/\.(ts|js)$/, '');
51
+ const mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ `${filePath}?t=${Date.now()}`));
52
+ const exported = mod.default;
53
+ if (!exported) {
54
+ log(`[scanner] ${name}: no default export, skipping`);
55
+ continue;
56
+ }
57
+ const entry = { path: filePath, name, mod: exported };
58
+ switch (exported.__type) {
59
+ case 'feed':
60
+ result.feeds.push(entry);
61
+ break;
62
+ case 'query':
63
+ result.queries.push(entry);
64
+ break;
65
+ case 'procedure':
66
+ result.procedures.push(entry);
67
+ break;
68
+ case 'hook':
69
+ result.hooks.push(entry);
70
+ break;
71
+ case 'setup':
72
+ result.setup.push(entry);
73
+ break;
74
+ case 'labels':
75
+ result.labels.push(entry);
76
+ break;
77
+ case 'og':
78
+ result.og.push(entry);
79
+ break;
80
+ case 'renderer':
81
+ result.renderer = entry;
82
+ break;
83
+ default:
84
+ log(`[scanner] ${name}: no recognized __type tag, skipping`);
85
+ }
86
+ }
87
+ return result;
88
+ }
package/dist/seed.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ /** Authenticated PDS session — returned by {@link seed.createAccount}. */
1
2
  export type Session = {
2
3
  did: string;
3
4
  accessJwt: string;
4
5
  handle: string;
5
6
  };
7
+ /** AT Protocol blob reference, as returned by `com.atproto.repo.uploadBlob`. */
6
8
  export type BlobRef = {
7
9
  $type: 'blob';
8
10
  ref: {
@@ -11,11 +13,23 @@ export type BlobRef = {
11
13
  mimeType: string;
12
14
  size: number;
13
15
  };
16
+ /** Options for the seed helper. All fields fall back to env vars or sensible defaults. */
14
17
  export type SeedOpts = {
15
18
  pds?: string;
16
19
  password?: string;
17
20
  lexicons?: string;
18
21
  };
22
+ /**
23
+ * Create a seed helper for populating a local PDS with test data.
24
+ *
25
+ * Returns `createAccount`, `createRecord`, and `uploadBlob` functions bound to
26
+ * the target PDS. Records are validated against the project's lexicons before
27
+ * being written. Generic parameter `R` maps collection NSIDs to their record types
28
+ * for type-safe seeding.
29
+ *
30
+ * @typeParam R - Map of collection NSID → record type (defaults to untyped)
31
+ * @param opts - PDS URL, password, and lexicon directory overrides
32
+ */
19
33
  export declare function seed<R extends Record<string, unknown> = Record<string, unknown>>(opts?: SeedOpts): {
20
34
  createAccount: (handle: string) => Promise<Session>;
21
35
  createRecord: <K extends keyof R & string>(session: Session, collection: K, record: R[K] extends Record<string, unknown> ? R[K] : Record<string, unknown>, opts: {
@@ -23,6 +37,11 @@ export declare function seed<R extends Record<string, unknown> = Record<string,
23
37
  }) => Promise<{
24
38
  uri: string;
25
39
  cid: string;
40
+ commit: {
41
+ cid: string;
42
+ rev: string;
43
+ };
44
+ validationStatus: string;
26
45
  }>;
27
46
  uploadBlob: (session: Session, filePath: string) => Promise<BlobRef>;
28
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AACxE,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAC/F,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7E,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ;4BAM1D,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;mBA4BlC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,WAC3C,OAAO,cACJ,CAAC,UACL,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QACvE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KACrB,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;0BA4BL,OAAO,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;EA4BhF"}
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AA8BA,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAExE,gFAAgF;AAChF,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/F,0FAA0F;AAC1F,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7E;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ;4BAO1D,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;mBA6BlC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,WAC3C,OAAO,cACJ,CAAC,UACL,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QACvE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KACrB,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC;0BAkCrE,OAAO,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;EA4BhF"}
package/dist/seed.js CHANGED
@@ -1,12 +1,49 @@
1
- import { loadLexicons } from "./schema.js";
1
+ /**
2
+ * Test data seeding helpers for populating a local PDS.
3
+ *
4
+ * Place a seed script at `seeds/seed.ts`. It runs during `hatk dev` to create
5
+ * accounts and records against your local PDS. Records are validated against
6
+ * your project's lexicons before being written.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // seeds/seed.ts
11
+ * import { seed } from '../hatk.generated.ts'
12
+ *
13
+ * const { createAccount, createRecord } = seed()
14
+ *
15
+ * const alice = await createAccount('alice.test')
16
+ * const bob = await createAccount('bob.test')
17
+ *
18
+ * await createRecord(
19
+ * alice,
20
+ * 'xyz.statusphere.status',
21
+ * { status: '👍', createdAt: new Date().toISOString() },
22
+ * { rkey: 'status1' },
23
+ * )
24
+ * ```
25
+ */
26
+ import { loadLexicons } from "./database/schema.js";
2
27
  import { validateRecord } from '@bigmoves/lexicon';
3
28
  import { resolve } from 'node:path';
4
29
  import { readFileSync } from 'node:fs';
30
+ /**
31
+ * Create a seed helper for populating a local PDS with test data.
32
+ *
33
+ * Returns `createAccount`, `createRecord`, and `uploadBlob` functions bound to
34
+ * the target PDS. Records are validated against the project's lexicons before
35
+ * being written. Generic parameter `R` maps collection NSIDs to their record types
36
+ * for type-safe seeding.
37
+ *
38
+ * @typeParam R - Map of collection NSID → record type (defaults to untyped)
39
+ * @param opts - PDS URL, password, and lexicon directory overrides
40
+ */
5
41
  export function seed(opts) {
6
42
  const pdsUrl = opts?.pds || process.env.PDS_URL || 'http://localhost:2583';
7
43
  const password = opts?.password || process.env.SEED_PASSWORD || 'password';
8
44
  const lexiconsDir = resolve(opts?.lexicons || 'lexicons');
9
45
  const lexiconArray = [...loadLexicons(lexiconsDir).values()];
46
+ /** Create a PDS account (or reuse an existing one) and return an authenticated session. */
10
47
  async function createAccount(handle) {
11
48
  const res = await fetch(`${pdsUrl}/xrpc/com.atproto.server.createAccount`, {
12
49
  method: 'POST',
@@ -34,6 +71,7 @@ export function seed(opts) {
34
71
  const session = (await sessionRes.json());
35
72
  return { ...session, handle };
36
73
  }
74
+ /** Validate a record against its lexicon and write it to the PDS via `putRecord`. */
37
75
  async function createRecord(session, collection, record, opts) {
38
76
  const error = validateRecord(lexiconArray, collection, record);
39
77
  if (error) {
@@ -53,10 +91,11 @@ export function seed(opts) {
53
91
  if (!res.ok) {
54
92
  throw new Error(`[seed] [${session.handle}] failed to create ${collection}: ${await res.text()}`);
55
93
  }
56
- const { uri, cid } = (await res.json());
57
- console.log(`[seed] [${session.handle}] ${collection} → ${uri}`);
58
- return { uri, cid };
94
+ const result = (await res.json());
95
+ console.log(`[seed] [${session.handle}] ${collection} → ${result.uri}`);
96
+ return result;
59
97
  }
98
+ /** Upload a file to the PDS as a blob. MIME type is inferred from the file extension. */
60
99
  async function uploadBlob(session, filePath) {
61
100
  const data = readFileSync(resolve(filePath));
62
101
  const ext = filePath.split('.').pop()?.toLowerCase() || '';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Scan the server/ directory and register all discovered handlers.
3
+ * Setup scripts run immediately (in sorted order).
4
+ */
5
+ export declare function initServer(serverDir: string, opts?: {
6
+ skipSetup?: boolean;
7
+ }): Promise<void>;
8
+ //# sourceMappingURL=server-init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-init.d.ts","sourceRoot":"","sources":["../src/server-init.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DjG"}
@@ -0,0 +1,62 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { log } from "./logger.js";
3
+ import { scanServerDir } from "./scanner.js";
4
+ import { registerFeed, listFeeds } from "./feeds.js";
5
+ import { registerXrpcHandler, listXrpc } from "./xrpc.js";
6
+ import { registerLabelModule, getLabelDefinitions, clearLabels } from "./labels.js";
7
+ import { registerOgHandler } from "./opengraph.js";
8
+ import { registerHook } from "./hooks.js";
9
+ import { runSetupHandler } from "./setup.js";
10
+ import { registerRenderer } from "./renderer.js";
11
+ /**
12
+ * Scan the server/ directory and register all discovered handlers.
13
+ * Setup scripts run immediately (in sorted order).
14
+ */
15
+ export async function initServer(serverDir, opts) {
16
+ if (!existsSync(serverDir)) {
17
+ log(`[server] No server/ directory found, skipping`);
18
+ return;
19
+ }
20
+ const scanned = await scanServerDir(serverDir);
21
+ // 1. Run setup scripts first (sorted by name) — skipped in test context
22
+ if (!opts?.skipSetup) {
23
+ for (const entry of scanned.setup.sort((a, b) => a.name.localeCompare(b.name))) {
24
+ await runSetupHandler(entry.name, entry.mod.handler);
25
+ }
26
+ }
27
+ // 2. Register feeds
28
+ for (const entry of scanned.feeds) {
29
+ const feedName = entry.name.includes('/') ? entry.name.split('/').pop() : entry.name;
30
+ registerFeed(feedName, entry.mod);
31
+ }
32
+ // 3. Register XRPC handlers
33
+ for (const entry of scanned.queries) {
34
+ registerXrpcHandler(entry.mod.nsid, entry.mod);
35
+ }
36
+ for (const entry of scanned.procedures) {
37
+ registerXrpcHandler(entry.mod.nsid, entry.mod);
38
+ }
39
+ // 4. Register hooks
40
+ for (const entry of scanned.hooks) {
41
+ registerHook(entry.mod.event, entry.mod.handler, entry.mod);
42
+ }
43
+ // 5. Register labels (clear first for hot-reload)
44
+ clearLabels();
45
+ for (const entry of scanned.labels) {
46
+ registerLabelModule(entry.name, entry.mod);
47
+ }
48
+ // 6. Register OG handlers
49
+ for (const entry of scanned.og) {
50
+ registerOgHandler(entry.mod);
51
+ }
52
+ // 7. Register renderer
53
+ if (scanned.renderer) {
54
+ registerRenderer(scanned.renderer.mod.handler);
55
+ }
56
+ log(`[server] Initialized from server/ directory:`);
57
+ log(` Feeds: ${listFeeds()
58
+ .map((f) => f.name)
59
+ .join(', ') || 'none'}`);
60
+ log(` XRPC: ${listXrpc().join(', ') || 'none'}`);
61
+ log(` Labels: ${getLabelDefinitions().length} definitions`);
62
+ }
package/dist/server.d.ts CHANGED
@@ -1,6 +1,29 @@
1
- import { type Server, type IncomingMessage } from 'node:http';
2
1
  import type { OAuthConfig } from './config.ts';
3
- export declare function startServer(port: number, collections: string[], publicDir: string | null, oauth: OAuthConfig | null, admins?: string[], resolveViewer?: (req: IncomingMessage) => {
2
+ /**
3
+ * Register built-in dev.hatk.* XRPC handlers in the handler registry.
4
+ * This makes them available to callXrpc() for use in SSR and server code.
5
+ */
6
+ export declare function registerCoreHandlers(collections: string[], oauth: OAuthConfig | null): void;
7
+ export interface HandlerConfig {
8
+ collections: string[];
9
+ publicDir: string | null;
10
+ oauth: OAuthConfig | null;
11
+ admins: string[];
12
+ renderer?: (request: Request, manifest: any) => Promise<{
13
+ html: string;
14
+ head?: string;
15
+ }>;
16
+ resolveViewer?: (request: Request) => {
17
+ did: string;
18
+ } | null;
19
+ onResync?: () => void;
20
+ }
21
+ /**
22
+ * Create a Web Standard request handler for all hatk routes.
23
+ * Returns a pure function: (Request) → Promise<Response>
24
+ */
25
+ export declare function createHandler(config: HandlerConfig): (request: Request) => Promise<Response>;
26
+ export declare function startServer(port: number, collections: string[], publicDir: string | null, oauth: OAuthConfig | null, admins?: string[], resolveViewer?: (request: Request) => {
4
27
  did: string;
5
- } | null): Server;
28
+ } | null, onResync?: () => void): import('node:http').Server;
6
29
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAA;AAmD3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA2B9C,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,MAAM,GAAE,MAAM,EAAO,EACrB,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAC/D,MAAM,CA28BR"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA2B9C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAgM3F;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxF,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA+zB5F;AAGD,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,MAAM,GAAE,MAAM,EAAO,EACrB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,GACpB,OAAO,WAAW,EAAE,MAAM,CAG5B"}