@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.
- package/dist/adapter.d.ts +19 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +108 -0
- package/dist/backfill.d.ts +2 -2
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +83 -41
- package/dist/car.d.ts +42 -10
- package/dist/car.d.ts.map +1 -1
- package/dist/car.js +154 -14
- package/dist/cli.js +243 -1043
- package/dist/config.d.ts +31 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -9
- package/dist/database/adapter-factory.d.ts +6 -0
- package/dist/database/adapter-factory.d.ts.map +1 -0
- package/dist/database/adapter-factory.js +20 -0
- package/dist/database/adapters/duckdb-search.d.ts +12 -0
- package/dist/database/adapters/duckdb-search.d.ts.map +1 -0
- package/dist/database/adapters/duckdb-search.js +27 -0
- package/dist/database/adapters/duckdb.d.ts +25 -0
- package/dist/database/adapters/duckdb.d.ts.map +1 -0
- package/dist/database/adapters/duckdb.js +161 -0
- package/dist/database/adapters/sqlite-search.d.ts +23 -0
- package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
- package/dist/database/adapters/sqlite-search.js +74 -0
- package/dist/database/adapters/sqlite.d.ts +18 -0
- package/dist/database/adapters/sqlite.d.ts.map +1 -0
- package/dist/database/adapters/sqlite.js +88 -0
- package/dist/{db.d.ts → database/db.d.ts} +57 -6
- package/dist/database/db.d.ts.map +1 -0
- package/dist/{db.js → database/db.js} +730 -549
- package/dist/database/dialect.d.ts +45 -0
- package/dist/database/dialect.d.ts.map +1 -0
- package/dist/database/dialect.js +72 -0
- package/dist/{fts.d.ts → database/fts.d.ts} +7 -0
- package/dist/database/fts.d.ts.map +1 -0
- package/dist/{fts.js → database/fts.js} +116 -32
- package/dist/database/index.d.ts +7 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +6 -0
- package/dist/database/ports.d.ts +50 -0
- package/dist/database/ports.d.ts.map +1 -0
- package/dist/database/ports.js +1 -0
- package/dist/{schema.d.ts → database/schema.d.ts} +14 -3
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/{schema.js → database/schema.js} +81 -41
- package/dist/dev-entry.d.ts +8 -0
- package/dist/dev-entry.d.ts.map +1 -0
- package/dist/dev-entry.js +113 -0
- package/dist/feeds.d.ts +12 -8
- package/dist/feeds.d.ts.map +1 -1
- package/dist/feeds.js +51 -6
- package/dist/hooks.d.ts +85 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +161 -0
- package/dist/hydrate.d.ts +7 -6
- package/dist/hydrate.d.ts.map +1 -1
- package/dist/hydrate.js +4 -16
- package/dist/indexer.d.ts +23 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +181 -34
- package/dist/labels.d.ts +36 -0
- package/dist/labels.d.ts.map +1 -1
- package/dist/labels.js +71 -6
- package/dist/lexicon-resolve.d.ts.map +1 -1
- package/dist/lexicon-resolve.js +27 -112
- package/dist/lexicons/com/atproto/label/defs.json +75 -0
- package/dist/lexicons/com/atproto/moderation/defs.json +30 -0
- package/dist/lexicons/com/atproto/repo/strongRef.json +24 -0
- package/dist/lexicons/dev/hatk/applyWrites.json +87 -0
- package/dist/lexicons/dev/hatk/createRecord.json +40 -0
- package/dist/lexicons/dev/hatk/createReport.json +48 -0
- package/dist/lexicons/dev/hatk/deleteRecord.json +25 -0
- package/dist/lexicons/dev/hatk/describeCollections.json +41 -0
- package/dist/lexicons/dev/hatk/describeFeeds.json +29 -0
- package/dist/lexicons/dev/hatk/describeLabels.json +45 -0
- package/dist/lexicons/dev/hatk/getFeed.json +30 -0
- package/dist/lexicons/dev/hatk/getPreferences.json +19 -0
- package/dist/lexicons/dev/hatk/getRecord.json +26 -0
- package/dist/lexicons/dev/hatk/getRecords.json +32 -0
- package/dist/lexicons/dev/hatk/putPreference.json +28 -0
- package/dist/lexicons/dev/hatk/putRecord.json +41 -0
- package/dist/lexicons/dev/hatk/searchRecords.json +32 -0
- package/dist/lexicons/dev/hatk/uploadBlob.json +23 -0
- package/dist/logger.d.ts +29 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +29 -0
- package/dist/main.js +138 -67
- package/dist/mst.d.ts +18 -1
- package/dist/mst.d.ts.map +1 -1
- package/dist/mst.js +19 -8
- package/dist/oauth/db.d.ts +3 -1
- package/dist/oauth/db.d.ts.map +1 -1
- package/dist/oauth/db.js +48 -19
- package/dist/oauth/server.d.ts +24 -0
- package/dist/oauth/server.d.ts.map +1 -1
- package/dist/oauth/server.js +198 -22
- package/dist/oauth/session.d.ts +11 -0
- package/dist/oauth/session.d.ts.map +1 -0
- package/dist/oauth/session.js +65 -0
- package/dist/opengraph.d.ts +10 -0
- package/dist/opengraph.d.ts.map +1 -1
- package/dist/opengraph.js +80 -40
- package/dist/pds-proxy.d.ts +60 -0
- package/dist/pds-proxy.d.ts.map +1 -0
- package/dist/pds-proxy.js +277 -0
- package/dist/push.d.ts +34 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +184 -0
- package/dist/renderer.d.ts +27 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +46 -0
- package/dist/resolve-hatk.d.ts +6 -0
- package/dist/resolve-hatk.d.ts.map +1 -0
- package/dist/resolve-hatk.js +20 -0
- package/dist/response.d.ts +16 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +69 -0
- package/dist/scanner.d.ts +21 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +88 -0
- package/dist/seed.d.ts +19 -0
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +43 -4
- package/dist/server-init.d.ts +8 -0
- package/dist/server-init.d.ts.map +1 -0
- package/dist/server-init.js +62 -0
- package/dist/server.d.ts +26 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +629 -635
- package/dist/setup.d.ts +28 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +50 -3
- package/dist/templates/feed.tpl +14 -0
- package/dist/templates/hook.tpl +5 -0
- package/dist/templates/label.tpl +15 -0
- package/dist/templates/og.tpl +17 -0
- package/dist/templates/seed.tpl +11 -0
- package/dist/templates/setup.tpl +5 -0
- package/dist/templates/test-feed.tpl +19 -0
- package/dist/templates/test-xrpc.tpl +19 -0
- package/dist/templates/xrpc.tpl +41 -0
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +39 -32
- package/dist/views.js +1 -1
- package/dist/vite-plugin.d.ts +1 -1
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +254 -66
- package/dist/xrpc.d.ts +75 -11
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +189 -39
- package/package.json +14 -7
- package/public/admin.html +133 -54
- package/dist/db.d.ts.map +0 -1
- package/dist/fts.d.ts.map +0 -1
- package/dist/oauth/hooks.d.ts +0 -10
- package/dist/oauth/hooks.d.ts.map +0 -1
- package/dist/oauth/hooks.js +0 -40
- package/dist/schema.d.ts.map +0 -1
- package/dist/test-browser.d.ts +0 -14
- package/dist/test-browser.d.ts.map +0 -1
- package/dist/test-browser.js +0 -26
package/dist/xrpc.js
CHANGED
|
@@ -6,12 +6,40 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* XRPC method handler system for serving AT Protocol endpoints.
|
|
11
|
+
*
|
|
12
|
+
* Place handler modules in the `xrpc/` directory, nested by NSID segments
|
|
13
|
+
* (e.g. `xrpc/app/bsky/feed/getAuthorFeed.ts` → `app.bsky.feed.getAuthorFeed`).
|
|
14
|
+
* Each module default-exports a `{ handler }` function that receives an
|
|
15
|
+
* {@link XrpcContext} with database access, query params, pagination, and
|
|
16
|
+
* viewer auth.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // xrpc/xyz/statusphere/getStatuses.ts
|
|
21
|
+
* import { defineXrpc } from '../../hatk.generated.ts'
|
|
22
|
+
*
|
|
23
|
+
* export default defineXrpc('xyz.statusphere.getStatuses', async (ctx) => {
|
|
24
|
+
* const rows = await ctx.db.query('SELECT * FROM statusphere_status LIMIT ?', [ctx.limit])
|
|
25
|
+
* return { statuses: rows }
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
9
29
|
import { resolve, relative } from 'node:path';
|
|
10
30
|
import { readdirSync, statSync } from 'node:fs';
|
|
11
|
-
import { log } from "./logger.js";
|
|
12
|
-
import { querySQL, runSQL, packCursor, unpackCursor, isTakendownDid, filterTakendownDids, searchRecords, findUriByFields,
|
|
13
|
-
import { resolveRecords } from "./hydrate.js";
|
|
14
|
-
import { getLexicon } from "./schema.js";
|
|
31
|
+
import { log, emit, timer } from "./logger.js";
|
|
32
|
+
import { querySQL, runSQL, packCursor, unpackCursor, isTakendownDid, filterTakendownDids, searchRecords, findUriByFields, } from "./database/db.js";
|
|
33
|
+
import { resolveRecords, buildBaseContext } from "./hydrate.js";
|
|
34
|
+
import { getLexicon } from "./database/schema.js";
|
|
35
|
+
import { createHmac } from 'node:crypto';
|
|
36
|
+
import { pdsCreateRecord, pdsPutRecord, pdsDeleteRecord, pdsApplyWrites } from "./pds-proxy.js";
|
|
37
|
+
let _oauthConfig = null;
|
|
38
|
+
/** Set the OAuth config used for record write helpers. Called once during boot. */
|
|
39
|
+
export function configureOAuth(config) {
|
|
40
|
+
_oauthConfig = config;
|
|
41
|
+
}
|
|
42
|
+
/** Thrown from XRPC handlers to return a 400 response with an error message. */
|
|
15
43
|
export class InvalidRequestError extends Error {
|
|
16
44
|
status = 400;
|
|
17
45
|
errorName;
|
|
@@ -20,6 +48,7 @@ export class InvalidRequestError extends Error {
|
|
|
20
48
|
this.errorName = errorName;
|
|
21
49
|
}
|
|
22
50
|
}
|
|
51
|
+
/** Thrown from XRPC handlers to return a 404 response. */
|
|
23
52
|
export class NotFoundError extends InvalidRequestError {
|
|
24
53
|
status = 404;
|
|
25
54
|
constructor(message = 'Not found') {
|
|
@@ -27,9 +56,36 @@ export class NotFoundError extends InvalidRequestError {
|
|
|
27
56
|
}
|
|
28
57
|
}
|
|
29
58
|
let _relayUrl = '';
|
|
59
|
+
let _cdn = null;
|
|
60
|
+
/** Set the relay URL used for blob URL generation. Called once during boot. */
|
|
30
61
|
export function configureRelay(relay) {
|
|
31
62
|
_relayUrl = relay;
|
|
32
63
|
}
|
|
64
|
+
/** Set the CDN config for imgproxy URL signing. Called once during boot. */
|
|
65
|
+
export function configureCdn(cdn) {
|
|
66
|
+
if (cdn) {
|
|
67
|
+
_cdn = {
|
|
68
|
+
url: cdn.url.replace(/\/$/, ''),
|
|
69
|
+
key: Buffer.from(cdn.key, 'hex'),
|
|
70
|
+
salt: Buffer.from(cdn.salt, 'hex'),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
_cdn = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Sign an imgproxy path with HMAC-SHA256 (URL-safe base64). */
|
|
78
|
+
function signPath(path) {
|
|
79
|
+
const hmac = createHmac('sha256', _cdn.key);
|
|
80
|
+
hmac.update(_cdn.salt);
|
|
81
|
+
hmac.update(path);
|
|
82
|
+
const sig = hmac.digest('base64url');
|
|
83
|
+
return `/${sig}${path}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate a CDN URL for a blob ref. Uses the PDS directly in local dev,
|
|
87
|
+
* a configured imgproxy CDN if available, or the Bluesky CDN as fallback.
|
|
88
|
+
*/
|
|
33
89
|
export function blobUrl(did, ref, preset = 'avatar') {
|
|
34
90
|
if (!ref)
|
|
35
91
|
return undefined;
|
|
@@ -39,9 +95,65 @@ export function blobUrl(did, ref, preset = 'avatar') {
|
|
|
39
95
|
if (_relayUrl.includes('localhost:2583')) {
|
|
40
96
|
return `http://localhost:2583/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${p.ref.$link}`;
|
|
41
97
|
}
|
|
98
|
+
if (_cdn) {
|
|
99
|
+
const path = `/${preset}/plain/${did}/${p.ref.$link}`;
|
|
100
|
+
return `${_cdn.url}${signPath(path)}`;
|
|
101
|
+
}
|
|
42
102
|
return `https://cdn.bsky.app/img/${preset}/plain/${did}/${p.ref.$link}@jpeg`;
|
|
43
103
|
}
|
|
104
|
+
/** Build a full XrpcContext from request parameters. Reuses buildBaseContext for shared fields. */
|
|
105
|
+
export function buildXrpcContext(params, cursor, limit, viewer, input) {
|
|
106
|
+
const base = buildBaseContext(viewer);
|
|
107
|
+
return {
|
|
108
|
+
...base,
|
|
109
|
+
db: { query: querySQL, run: runSQL },
|
|
110
|
+
params,
|
|
111
|
+
input: input || {},
|
|
112
|
+
cursor,
|
|
113
|
+
limit,
|
|
114
|
+
packCursor,
|
|
115
|
+
unpackCursor,
|
|
116
|
+
isTakendown: isTakendownDid,
|
|
117
|
+
filterTakendownDids,
|
|
118
|
+
search: searchRecords,
|
|
119
|
+
resolve: resolveRecords,
|
|
120
|
+
exists: async (collection, filters) => {
|
|
121
|
+
const conditions = Object.entries(filters).map(([field, value]) => ({ field, value }));
|
|
122
|
+
const uri = await findUriByFields(collection, conditions);
|
|
123
|
+
return uri !== null;
|
|
124
|
+
},
|
|
125
|
+
createRecord: async (collection, record, opts) => {
|
|
126
|
+
if (!_oauthConfig)
|
|
127
|
+
throw new Error('No OAuth config — cannot write to PDS');
|
|
128
|
+
if (!viewer)
|
|
129
|
+
throw new Error('Authentication required to write records');
|
|
130
|
+
return pdsCreateRecord(_oauthConfig, viewer, { collection, record, rkey: opts?.rkey });
|
|
131
|
+
},
|
|
132
|
+
putRecord: async (collection, rkey, record) => {
|
|
133
|
+
if (!_oauthConfig)
|
|
134
|
+
throw new Error('No OAuth config — cannot write to PDS');
|
|
135
|
+
if (!viewer)
|
|
136
|
+
throw new Error('Authentication required to write records');
|
|
137
|
+
return pdsPutRecord(_oauthConfig, viewer, { collection, rkey, record });
|
|
138
|
+
},
|
|
139
|
+
deleteRecord: async (collection, rkey) => {
|
|
140
|
+
if (!_oauthConfig)
|
|
141
|
+
throw new Error('No OAuth config — cannot write to PDS');
|
|
142
|
+
if (!viewer)
|
|
143
|
+
throw new Error('Authentication required to write records');
|
|
144
|
+
await pdsDeleteRecord(_oauthConfig, viewer, { collection, rkey });
|
|
145
|
+
},
|
|
146
|
+
applyWrites: async (writes) => {
|
|
147
|
+
if (!_oauthConfig)
|
|
148
|
+
throw new Error('No OAuth config — cannot write to PDS');
|
|
149
|
+
if (!viewer)
|
|
150
|
+
throw new Error('Authentication required to write records');
|
|
151
|
+
return pdsApplyWrites(_oauthConfig, viewer, { writes });
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
44
155
|
const handlers = new Map();
|
|
156
|
+
/** Recursively collect .ts/.js files in a directory, skipping files prefixed with `_`. */
|
|
45
157
|
function walkDir(dir) {
|
|
46
158
|
const results = [];
|
|
47
159
|
try {
|
|
@@ -58,6 +170,11 @@ function walkDir(dir) {
|
|
|
58
170
|
catch { }
|
|
59
171
|
return results.sort();
|
|
60
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Discover and load XRPC handler modules from the `xrpc/` directory.
|
|
175
|
+
* Directory nesting maps to NSID segments. Parameters are validated and
|
|
176
|
+
* coerced against the matching lexicon definition.
|
|
177
|
+
*/
|
|
61
178
|
export async function initXrpc(xrpcDir) {
|
|
62
179
|
const files = walkDir(xrpcDir);
|
|
63
180
|
if (files.length === 0)
|
|
@@ -65,7 +182,7 @@ export async function initXrpc(xrpcDir) {
|
|
|
65
182
|
for (const scriptPath of files) {
|
|
66
183
|
const rel = relative(xrpcDir, scriptPath).replace(/\.(ts|js)$/, '');
|
|
67
184
|
const name = rel.replace(/[\\/]/g, '.');
|
|
68
|
-
const mod = await import(__rewriteRelativeImportExtension(scriptPath));
|
|
185
|
+
const mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ `${scriptPath}?t=${Date.now()}`));
|
|
69
186
|
const handler = mod.default;
|
|
70
187
|
// Extract param schema from lexicon for validation and defaults
|
|
71
188
|
const lexicon = getLexicon(name);
|
|
@@ -89,51 +206,84 @@ export async function initXrpc(xrpcDir) {
|
|
|
89
206
|
throw new InvalidRequestError(`Missing required parameter: ${param}`, 'InvalidRequest');
|
|
90
207
|
}
|
|
91
208
|
}
|
|
92
|
-
const ctx =
|
|
93
|
-
db: { query: querySQL, run: runSQL },
|
|
94
|
-
params,
|
|
95
|
-
input: input || {},
|
|
96
|
-
cursor,
|
|
97
|
-
limit,
|
|
98
|
-
viewer,
|
|
99
|
-
packCursor,
|
|
100
|
-
unpackCursor,
|
|
101
|
-
isTakendown: isTakendownDid,
|
|
102
|
-
filterTakendownDids,
|
|
103
|
-
search: searchRecords,
|
|
104
|
-
resolve: resolveRecords,
|
|
105
|
-
lookup: async (collection, field, values) => {
|
|
106
|
-
if (values.length === 0)
|
|
107
|
-
return new Map();
|
|
108
|
-
const unique = [...new Set(values.filter(Boolean))];
|
|
109
|
-
return lookupByFieldBatch(collection, field, unique);
|
|
110
|
-
},
|
|
111
|
-
count: async (collection, field, values) => {
|
|
112
|
-
if (values.length === 0)
|
|
113
|
-
return new Map();
|
|
114
|
-
const unique = [...new Set(values.filter(Boolean))];
|
|
115
|
-
return countByFieldBatch(collection, field, unique);
|
|
116
|
-
},
|
|
117
|
-
exists: async (collection, filters) => {
|
|
118
|
-
const conditions = Object.entries(filters).map(([field, value]) => ({ field, value }));
|
|
119
|
-
const uri = await findUriByFields(collection, conditions);
|
|
120
|
-
return uri !== null;
|
|
121
|
-
},
|
|
122
|
-
labels: queryLabelsForUris,
|
|
123
|
-
blobUrl,
|
|
124
|
-
};
|
|
209
|
+
const ctx = buildXrpcContext(params, cursor, limit, viewer, input);
|
|
125
210
|
return handler.handler(ctx);
|
|
126
211
|
},
|
|
127
212
|
});
|
|
128
213
|
log(`[xrpc] discovered: ${name}`);
|
|
129
214
|
}
|
|
130
215
|
}
|
|
216
|
+
/** Register a single XRPC handler from a scanned server/ module. */
|
|
217
|
+
export function registerXrpcHandler(nsid, handlerModule) {
|
|
218
|
+
const lexicon = getLexicon(nsid);
|
|
219
|
+
const paramsDef = lexicon?.defs?.main?.parameters;
|
|
220
|
+
const requiredParams = paramsDef?.required || [];
|
|
221
|
+
const paramProperties = paramsDef?.properties || {};
|
|
222
|
+
handlers.set(nsid, {
|
|
223
|
+
name: nsid,
|
|
224
|
+
execute: async (params, cursor, limit, viewer, input) => {
|
|
225
|
+
for (const [key, def] of Object.entries(paramProperties)) {
|
|
226
|
+
if (params[key] == null && def.default != null) {
|
|
227
|
+
params[key] = String(def.default);
|
|
228
|
+
}
|
|
229
|
+
if (params[key] != null && def.type === 'integer') {
|
|
230
|
+
params[key] = Number(params[key]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const param of requiredParams) {
|
|
234
|
+
if (!params[param]) {
|
|
235
|
+
throw new InvalidRequestError(`Missing required parameter: ${param}`, 'InvalidRequest');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const ctx = buildXrpcContext(params, cursor, limit, viewer, input);
|
|
239
|
+
return handlerModule.handler(ctx);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/** Execute a registered XRPC handler by name. Returns null if no handler matches. */
|
|
131
244
|
export async function executeXrpc(name, params, cursor, limit, viewer, input) {
|
|
132
245
|
const handler = handlers.get(name);
|
|
133
246
|
if (!handler)
|
|
134
247
|
return null;
|
|
135
|
-
|
|
248
|
+
const elapsed = timer();
|
|
249
|
+
try {
|
|
250
|
+
const result = await handler.execute(params, cursor, limit, viewer || null, input);
|
|
251
|
+
emit('xrpc', name, { duration_ms: elapsed(), params, cursor, limit, viewer: viewer?.did });
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
emit('xrpc', name, { duration_ms: elapsed(), params, cursor, limit, viewer: viewer?.did, error: err.message });
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/** Call a registered XRPC handler directly (no HTTP). For use in SSR renderers. */
|
|
260
|
+
export async function callXrpc(nsid, params = {}, input) {
|
|
261
|
+
const viewer = globalThis.__hatk_viewer ?? null;
|
|
262
|
+
// In externalized module context (e.g. SSR), delegate to the runner's callXrpc via globalThis.
|
|
263
|
+
// The runner's module instance has all registered handlers; this (Node's) instance may not.
|
|
264
|
+
if (handlers.size === 0 && globalThis.__hatk_callXrpc) {
|
|
265
|
+
return globalThis.__hatk_callXrpc(nsid, params, input);
|
|
266
|
+
}
|
|
267
|
+
const stringParams = {};
|
|
268
|
+
for (const [k, v] of Object.entries(params)) {
|
|
269
|
+
if (v != null)
|
|
270
|
+
stringParams[k] = String(v);
|
|
271
|
+
}
|
|
272
|
+
const limit = params.limit ? Number(params.limit) : 20;
|
|
273
|
+
const cursor = params.cursor ?? undefined;
|
|
274
|
+
const result = await executeXrpc(nsid, stringParams, cursor, limit, viewer, input);
|
|
275
|
+
if (result === null)
|
|
276
|
+
throw new Error(`No XRPC handler registered for ${nsid}`);
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Register a core XRPC handler directly (no XrpcContext wrapping).
|
|
281
|
+
* Used for built-in dev.hatk.* handlers that manage their own dependencies.
|
|
282
|
+
*/
|
|
283
|
+
export function registerCoreXrpcHandler(nsid, fn) {
|
|
284
|
+
handlers.set(nsid, { name: nsid, execute: fn });
|
|
136
285
|
}
|
|
286
|
+
/** Return all registered XRPC method names. */
|
|
137
287
|
export function listXrpc() {
|
|
138
288
|
return Array.from(handlers.keys());
|
|
139
289
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatk/hatk",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.61",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hatk": "dist/cli.js"
|
|
@@ -20,27 +20,34 @@
|
|
|
20
20
|
"./xrpc-client": "./dist/xrpc-client.js",
|
|
21
21
|
"./views": "./dist/views.js",
|
|
22
22
|
"./seed": "./dist/seed.js",
|
|
23
|
+
"./hooks": "./dist/hooks.js",
|
|
23
24
|
"./setup": "./dist/setup.js",
|
|
24
25
|
"./test": "./dist/test.js",
|
|
25
|
-
"./
|
|
26
|
-
"./vite-plugin": "./dist/vite-plugin.js"
|
|
26
|
+
"./config": "./dist/config.js",
|
|
27
|
+
"./vite-plugin": "./dist/vite-plugin.js",
|
|
28
|
+
"./renderer": "./dist/renderer.js"
|
|
27
29
|
},
|
|
28
30
|
"scripts": {
|
|
29
|
-
"
|
|
31
|
+
"clean": "rm -rf dist",
|
|
32
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && cp -r src/templates dist/templates && cp -r src/lexicons dist/lexicons",
|
|
30
33
|
"prepublishOnly": "npm run build"
|
|
31
34
|
},
|
|
32
35
|
"dependencies": {
|
|
33
|
-
"@bigmoves/lexicon": "^0.2.
|
|
36
|
+
"@bigmoves/lexicon": "^0.2.2",
|
|
34
37
|
"@duckdb/node-api": "^1.4.4-r.1",
|
|
35
38
|
"@hatk/oauth-client": "*",
|
|
36
39
|
"@resvg/resvg-js": "^2.6.2",
|
|
40
|
+
"better-sqlite3": "^12.6.2",
|
|
37
41
|
"satori": "^0.19.2",
|
|
38
42
|
"vitest": "^4",
|
|
39
43
|
"yaml": "^2.7.0"
|
|
40
44
|
},
|
|
41
45
|
"devDependencies": {
|
|
42
|
-
"@
|
|
46
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
43
47
|
"@types/react": "^19.2.14",
|
|
44
|
-
"vite": "^
|
|
48
|
+
"vite": "^8.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"vite": "^8.0.0"
|
|
45
52
|
}
|
|
46
53
|
}
|
package/public/admin.html
CHANGED
|
@@ -783,23 +783,6 @@
|
|
|
783
783
|
font-size: 1rem;
|
|
784
784
|
}
|
|
785
785
|
|
|
786
|
-
/* ── Schema ── */
|
|
787
|
-
.schema-pre {
|
|
788
|
-
font-family: var(--mono);
|
|
789
|
-
font-size: 0.8rem;
|
|
790
|
-
line-height: 1.6;
|
|
791
|
-
padding: 1rem;
|
|
792
|
-
margin: 0;
|
|
793
|
-
background: var(--bg-recessed);
|
|
794
|
-
border-radius: 0 0 6px 6px;
|
|
795
|
-
white-space: pre-wrap;
|
|
796
|
-
word-break: break-word;
|
|
797
|
-
color: var(--text);
|
|
798
|
-
overflow-x: auto;
|
|
799
|
-
}
|
|
800
|
-
.schema-section {
|
|
801
|
-
margin-bottom: 1.5rem;
|
|
802
|
-
}
|
|
803
786
|
.loading {
|
|
804
787
|
color: var(--text-3);
|
|
805
788
|
font-size: 0.9375rem;
|
|
@@ -1220,7 +1203,7 @@
|
|
|
1220
1203
|
<button class="tab active" data-tab="overview">Overview</button>
|
|
1221
1204
|
<button class="tab" data-tab="repos">Repos</button>
|
|
1222
1205
|
<button class="tab" data-tab="content">Content</button>
|
|
1223
|
-
<button class="tab" data-tab="
|
|
1206
|
+
<button class="tab" data-tab="reports">Reports</button>
|
|
1224
1207
|
</nav>
|
|
1225
1208
|
|
|
1226
1209
|
<!-- Overview -->
|
|
@@ -1277,10 +1260,6 @@
|
|
|
1277
1260
|
<div id="repos-results"><div class="loading">Loading</div></div>
|
|
1278
1261
|
</div>
|
|
1279
1262
|
|
|
1280
|
-
<!-- Schema -->
|
|
1281
|
-
<div class="tab-panel" id="panel-schema">
|
|
1282
|
-
<div id="schema-results"><div class="loading">Loading</div></div>
|
|
1283
|
-
</div>
|
|
1284
1263
|
|
|
1285
1264
|
<!-- Content -->
|
|
1286
1265
|
<div class="tab-panel" id="panel-content">
|
|
@@ -1299,6 +1278,21 @@
|
|
|
1299
1278
|
<div class="loading">Loading</div>
|
|
1300
1279
|
</div>
|
|
1301
1280
|
</div>
|
|
1281
|
+
|
|
1282
|
+
<!-- Reports -->
|
|
1283
|
+
<div class="tab-panel" id="panel-reports">
|
|
1284
|
+
<div class="search-bar">
|
|
1285
|
+
<select class="search-input" id="reports-status" style="max-width: 200px">
|
|
1286
|
+
<option value="open">Open</option>
|
|
1287
|
+
<option value="resolved">Resolved</option>
|
|
1288
|
+
<option value="dismissed">Dismissed</option>
|
|
1289
|
+
</select>
|
|
1290
|
+
<select class="search-input" id="reports-label-filter" style="max-width: 200px">
|
|
1291
|
+
<option value="">All labels</option>
|
|
1292
|
+
</select>
|
|
1293
|
+
</div>
|
|
1294
|
+
<div id="reports-results"></div>
|
|
1295
|
+
</div>
|
|
1302
1296
|
</div>
|
|
1303
1297
|
|
|
1304
1298
|
<!-- Bottom nav (mobile) -->
|
|
@@ -1307,7 +1301,7 @@
|
|
|
1307
1301
|
<button class="bnav-btn active" data-tab="overview">Overview</button>
|
|
1308
1302
|
<button class="bnav-btn" data-tab="repos">Repos</button>
|
|
1309
1303
|
<button class="bnav-btn" data-tab="content">Content</button>
|
|
1310
|
-
<button class="bnav-btn" data-tab="
|
|
1304
|
+
<button class="bnav-btn" data-tab="reports">Reports</button>
|
|
1311
1305
|
</div>
|
|
1312
1306
|
</div>
|
|
1313
1307
|
</div>
|
|
@@ -1490,8 +1484,8 @@
|
|
|
1490
1484
|
document.getElementById(`panel-${tab}`).classList.add('active')
|
|
1491
1485
|
if (tab === 'overview') loadOverview()
|
|
1492
1486
|
if (tab === 'repos') loadRepos()
|
|
1493
|
-
if (tab === 'schema') loadSchema()
|
|
1494
1487
|
if (tab === 'content') loadContent()
|
|
1488
|
+
if (tab === 'reports') loadReports()
|
|
1495
1489
|
if (push) pushURL({ tab, status: '', q: '', offset: 0, cq: '' })
|
|
1496
1490
|
}
|
|
1497
1491
|
|
|
@@ -1518,6 +1512,7 @@
|
|
|
1518
1512
|
<div class="stat-card"><div class="stat-label">Pending</div><div class="stat-value yellow">${fmt(repoStatuses.pending)}</div></div>
|
|
1519
1513
|
<div class="stat-card"><div class="stat-label">Failed</div><div class="stat-value red">${fmt(repoStatuses.failed)}</div></div>
|
|
1520
1514
|
<div class="stat-card"><div class="stat-label">Taken Down</div><div class="stat-value red">${fmt(repoStatuses.takendown)}</div></div>
|
|
1515
|
+
${info.openReports > 0 ? `<div class="stat-card" style="cursor:pointer" onclick="activateTab('reports')"><div class="stat-label">Open Reports</div><div class="stat-value yellow">${fmt(info.openReports)}</div></div>` : ''}
|
|
1521
1516
|
`
|
|
1522
1517
|
|
|
1523
1518
|
const collectionCards = document.getElementById('collection-cards')
|
|
@@ -1599,36 +1594,6 @@
|
|
|
1599
1594
|
}
|
|
1600
1595
|
})
|
|
1601
1596
|
|
|
1602
|
-
// ── Schema ──
|
|
1603
|
-
|
|
1604
|
-
async function loadSchema() {
|
|
1605
|
-
const container = document.getElementById('schema-results')
|
|
1606
|
-
try {
|
|
1607
|
-
const data = await api('/admin/schema')
|
|
1608
|
-
let html = ''
|
|
1609
|
-
|
|
1610
|
-
// Lexicons section
|
|
1611
|
-
if (data.lexicons && data.lexicons.length) {
|
|
1612
|
-
html += '<div class="schema-section"><div class="section-label">Lexicons</div>'
|
|
1613
|
-
for (const lex of data.lexicons) {
|
|
1614
|
-
html += `<div class="card" style="margin-bottom:0.5rem;"><div style="font-family:var(--mono);font-size:0.8rem;font-weight:600;padding:0.5rem 0.75rem;border-bottom:1px solid var(--border);">${escapeHtml(lex.nsid)}</div><pre class="schema-pre">${escapeHtml(JSON.stringify(lex.lexicon, null, 2))}</pre></div>`
|
|
1615
|
-
}
|
|
1616
|
-
html += '</div>'
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
// DDL section
|
|
1620
|
-
if (data.ddl) {
|
|
1621
|
-
html += '<div class="schema-section"><div class="section-label">Tables (DuckDB DDL)</div>'
|
|
1622
|
-
html += `<div class="card"><pre class="schema-pre">${escapeHtml(data.ddl)}</pre></div>`
|
|
1623
|
-
html += '</div>'
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
container.innerHTML = html || '<div class="empty-state">No schema found</div>'
|
|
1627
|
-
} catch (e) {
|
|
1628
|
-
container.innerHTML = `<div class="empty-state">${escapeHtml(e.message)}</div>`
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
1597
|
// ── Repos ──
|
|
1633
1598
|
|
|
1634
1599
|
let reposLoaded = false
|
|
@@ -2161,6 +2126,120 @@
|
|
|
2161
2126
|
})
|
|
2162
2127
|
})
|
|
2163
2128
|
}
|
|
2129
|
+
|
|
2130
|
+
// ── Reports ──
|
|
2131
|
+
|
|
2132
|
+
const reportsPage = { limit: 50, offset: 0 }
|
|
2133
|
+
|
|
2134
|
+
function populateReportsLabelFilter() {
|
|
2135
|
+
const select = document.getElementById('reports-label-filter')
|
|
2136
|
+
const current = select.value
|
|
2137
|
+
select.innerHTML = '<option value="">All labels</option>' +
|
|
2138
|
+
labelDefinitions.map(d => `<option value="${d.identifier}">${d.identifier}</option>`).join('')
|
|
2139
|
+
select.value = current
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
document.getElementById('reports-status').addEventListener('change', () => {
|
|
2143
|
+
reportsPage.offset = 0
|
|
2144
|
+
loadReports()
|
|
2145
|
+
})
|
|
2146
|
+
document.getElementById('reports-label-filter').addEventListener('change', () => {
|
|
2147
|
+
reportsPage.offset = 0
|
|
2148
|
+
loadReports()
|
|
2149
|
+
})
|
|
2150
|
+
|
|
2151
|
+
async function loadReports() {
|
|
2152
|
+
populateReportsLabelFilter()
|
|
2153
|
+
const status = document.getElementById('reports-status').value
|
|
2154
|
+
const label = document.getElementById('reports-label-filter').value
|
|
2155
|
+
const container = document.getElementById('reports-results')
|
|
2156
|
+
container.innerHTML = '<div class="loading">Loading</div>'
|
|
2157
|
+
try {
|
|
2158
|
+
let url = `/admin/reports?status=${status}&limit=${reportsPage.limit}&offset=${reportsPage.offset}`
|
|
2159
|
+
if (label) url += `&label=${encodeURIComponent(label)}`
|
|
2160
|
+
const result = await api(url)
|
|
2161
|
+
renderReports(result.reports || [], result.total)
|
|
2162
|
+
} catch (e) {
|
|
2163
|
+
container.innerHTML = `<div class="empty-state">${escapeHtml(e.message)}</div>`
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
function renderReports(reports, total) {
|
|
2168
|
+
const container = document.getElementById('reports-results')
|
|
2169
|
+
if (!reports.length) {
|
|
2170
|
+
container.innerHTML = '<div class="empty-state">No reports found</div>'
|
|
2171
|
+
return
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
const showPagination = total != null && total > reportsPage.limit
|
|
2175
|
+
const paginationHtml = showPagination ? `
|
|
2176
|
+
<div class="pagination">
|
|
2177
|
+
<span>${reportsPage.offset + 1}\u2013${Math.min(reportsPage.offset + reportsPage.limit, total)} of ${total.toLocaleString()}</span>
|
|
2178
|
+
<div class="pagination-buttons">
|
|
2179
|
+
<button class="btn btn-sm" data-reports-page="prev" ${reportsPage.offset === 0 ? 'disabled' : ''}>Prev</button>
|
|
2180
|
+
<button class="btn btn-sm" data-reports-page="next" ${reportsPage.offset + reportsPage.limit >= total ? 'disabled' : ''}>Next</button>
|
|
2181
|
+
</div>
|
|
2182
|
+
</div>
|
|
2183
|
+
` : ''
|
|
2184
|
+
|
|
2185
|
+
const countLabel = total != null
|
|
2186
|
+
? `${total.toLocaleString()} report${total !== 1 ? 's' : ''}`
|
|
2187
|
+
: `${reports.length} result${reports.length !== 1 ? 's' : ''}`
|
|
2188
|
+
|
|
2189
|
+
const isOpen = document.getElementById('reports-status').value === 'open'
|
|
2190
|
+
|
|
2191
|
+
container.innerHTML = `
|
|
2192
|
+
<div class="card">
|
|
2193
|
+
<div class="result-count">${countLabel}</div>
|
|
2194
|
+
${reports.map(r => {
|
|
2195
|
+
const reporterDisplay = r.reported_by_handle ? `@${escapeHtml(r.reported_by_handle)}` : escapeHtml(r.reported_by)
|
|
2196
|
+
const date = new Date(r.created_at).toLocaleString()
|
|
2197
|
+
return `<div class="record-card">
|
|
2198
|
+
<div class="record-header">
|
|
2199
|
+
<div class="record-meta">
|
|
2200
|
+
<div class="record-uri" title="${escapeHtml(r.subject_uri)}">${escapeHtml(r.subject_uri)}</div>
|
|
2201
|
+
<div class="record-summary">
|
|
2202
|
+
<span class="label-tag">${escapeHtml(r.label)}</span>
|
|
2203
|
+
reported by ${reporterDisplay} · ${date}
|
|
2204
|
+
</div>
|
|
2205
|
+
${r.reason ? `<div class="record-summary" style="margin-top:0.25rem">${escapeHtml(r.reason)}</div>` : ''}
|
|
2206
|
+
${!isOpen ? `<div class="record-summary" style="margin-top:0.25rem;opacity:0.6">${escapeHtml(r.status)}${r.resolved_by ? ` by ${escapeHtml(r.resolved_by)}` : ''}</div>` : ''}
|
|
2207
|
+
</div>
|
|
2208
|
+
${isOpen ? `<div class="record-actions">
|
|
2209
|
+
<button class="btn btn-sm" data-action="resolve-report" data-id="${r.id}" data-resolve="resolve" style="background:var(--accent);color:white">Apply Label</button>
|
|
2210
|
+
<button class="btn btn-sm" data-action="resolve-report" data-id="${r.id}" data-resolve="dismiss">Dismiss</button>
|
|
2211
|
+
</div>` : ''}
|
|
2212
|
+
</div>
|
|
2213
|
+
</div>`
|
|
2214
|
+
}).join('')}
|
|
2215
|
+
${paginationHtml}
|
|
2216
|
+
</div>
|
|
2217
|
+
`
|
|
2218
|
+
|
|
2219
|
+
container.querySelectorAll('[data-reports-page="prev"]').forEach(b => {
|
|
2220
|
+
b.addEventListener('click', () => { reportsPage.offset = Math.max(0, reportsPage.offset - reportsPage.limit); loadReports() })
|
|
2221
|
+
})
|
|
2222
|
+
container.querySelectorAll('[data-reports-page="next"]').forEach(b => {
|
|
2223
|
+
b.addEventListener('click', () => { reportsPage.offset += reportsPage.limit; loadReports() })
|
|
2224
|
+
})
|
|
2225
|
+
|
|
2226
|
+
container.querySelectorAll('[data-action="resolve-report"]').forEach(btn => {
|
|
2227
|
+
btn.addEventListener('click', async () => {
|
|
2228
|
+
const action = btn.dataset.resolve
|
|
2229
|
+
try {
|
|
2230
|
+
await api('/admin/reports/resolve', {
|
|
2231
|
+
method: 'POST',
|
|
2232
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2233
|
+
body: JSON.stringify({ id: parseInt(btn.dataset.id), action }),
|
|
2234
|
+
})
|
|
2235
|
+
toast(action === 'resolve' ? 'Label applied & report resolved' : 'Report dismissed', 'success')
|
|
2236
|
+
loadReports()
|
|
2237
|
+
} catch (e) {
|
|
2238
|
+
toast(e.message, 'error')
|
|
2239
|
+
}
|
|
2240
|
+
})
|
|
2241
|
+
})
|
|
2242
|
+
}
|
|
2164
2243
|
</script>
|
|
2165
2244
|
</body>
|
|
2166
2245
|
</html>
|
package/dist/db.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/fts.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../src/fts.ts"],"names":[],"mappings":"AAwEA,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAE7D;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAElE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0FrE;AAokBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB5E"}
|
package/dist/oauth/hooks.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** onLogin hook: called after each successful OAuth login. */
|
|
2
|
-
export type OnLoginCtx = {
|
|
3
|
-
did: string;
|
|
4
|
-
ensureRepo: (did: string) => Promise<void>;
|
|
5
|
-
};
|
|
6
|
-
/** Load on-login hook from the exercise's hooks/ directory. */
|
|
7
|
-
export declare function loadOnLoginHook(hooksDir: string): Promise<void>;
|
|
8
|
-
/** Fire the onLogin hook if loaded. Errors are logged but don't block login. */
|
|
9
|
-
export declare function fireOnLoginHook(did: string): Promise<void>;
|
|
10
|
-
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/oauth/hooks.ts"],"names":[],"mappings":"AAMA,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3C,CAAA;AAMD,+DAA+D;AAC/D,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrE;AAOD,gFAAgF;AAChF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhE"}
|
package/dist/oauth/hooks.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
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 { existsSync } from 'node:fs';
|
|
10
|
-
import { resolve } from 'node:path';
|
|
11
|
-
import { log } from "../logger.js";
|
|
12
|
-
import { setRepoStatus } from "../db.js";
|
|
13
|
-
import { triggerAutoBackfill } from "../indexer.js";
|
|
14
|
-
let onLoginHook = null;
|
|
15
|
-
/** Load on-login hook from the exercise's hooks/ directory. */
|
|
16
|
-
export async function loadOnLoginHook(hooksDir) {
|
|
17
|
-
const tsPath = resolve(hooksDir, 'on-login.ts');
|
|
18
|
-
const jsPath = resolve(hooksDir, 'on-login.js');
|
|
19
|
-
const path = existsSync(tsPath) ? tsPath : existsSync(jsPath) ? jsPath : null;
|
|
20
|
-
if (!path)
|
|
21
|
-
return;
|
|
22
|
-
const mod = await import(__rewriteRelativeImportExtension(path));
|
|
23
|
-
onLoginHook = mod.default;
|
|
24
|
-
log('[oauth] on-login hook loaded');
|
|
25
|
-
}
|
|
26
|
-
async function ensureRepo(did) {
|
|
27
|
-
await setRepoStatus(did, 'pending');
|
|
28
|
-
triggerAutoBackfill(did);
|
|
29
|
-
}
|
|
30
|
-
/** Fire the onLogin hook if loaded. Errors are logged but don't block login. */
|
|
31
|
-
export async function fireOnLoginHook(did) {
|
|
32
|
-
if (!onLoginHook)
|
|
33
|
-
return;
|
|
34
|
-
try {
|
|
35
|
-
await onLoginHook({ did, ensureRepo });
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
console.error('[oauth] onLogin hook error:', err.message);
|
|
39
|
-
}
|
|
40
|
-
}
|