@hatk/hatk 0.0.1-alpha.80 → 0.0.1-alpha.81
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.map +1 -1
- package/dist/adapter.js +1 -0
- package/dist/backfill.d.ts +8 -0
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +34 -14
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/database/adapters/sqlite-search.d.ts +0 -7
- package/dist/database/adapters/sqlite-search.d.ts.map +1 -1
- package/dist/database/adapters/sqlite-search.js +7 -5
- package/dist/dev-entry.d.ts.map +1 -1
- package/dist/dev-entry.js +9 -3
- package/dist/indexer.d.ts +25 -1
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +70 -2
- package/dist/main.js +8 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -1
- package/dist/xrpc.d.ts +2 -0
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +15 -2
- package/package.json +1 -1
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAgB,MAAM,WAAW,CAAA;AAEnF;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CA0BrE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzF;AAED,wFAAwF;AACxF,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAgB,MAAM,WAAW,CAAA;AAEnF;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CA0BrE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzF;AAED,wFAAwF;AACxF,eAAO,MAAM,WAAW,UAiBvB,CAAA;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,EAChD,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,6EA4BjF"}
|
package/dist/adapter.js
CHANGED
|
@@ -58,6 +58,7 @@ export async function sendResponse(res, response) {
|
|
|
58
58
|
/** Routes handled by hatk — everything else can fall through to a framework handler. */
|
|
59
59
|
export const HATK_ROUTES = [
|
|
60
60
|
'/xrpc/',
|
|
61
|
+
'/blob/', // dev image proxy — see blobUrl in xrpc.ts
|
|
61
62
|
'/oauth/',
|
|
62
63
|
'/oauth-client-metadata.json',
|
|
63
64
|
'/.well-known/oauth-authorization-server',
|
package/dist/backfill.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ import type { BackfillConfig } from './config.ts';
|
|
|
22
22
|
interface BackfillOpts {
|
|
23
23
|
/** Base URL of the relay or PDS to enumerate repos from (e.g. `wss://bsky.network`). */
|
|
24
24
|
pdsUrl: string;
|
|
25
|
+
/** Further relays/PDSes to enumerate — the HTTP side of `config.relays`. */
|
|
26
|
+
extraPdsUrls?: string[];
|
|
25
27
|
/** PLC directory URL used to resolve `did:plc` identifiers (e.g. `https://plc.directory`). */
|
|
26
28
|
plcUrl: string;
|
|
27
29
|
/** AT Protocol collection NSIDs to index (e.g. `app.bsky.feed.post`). */
|
|
@@ -29,6 +31,12 @@ interface BackfillOpts {
|
|
|
29
31
|
/** Backfill behavior settings from `hatk.config.ts`. */
|
|
30
32
|
config: BackfillConfig;
|
|
31
33
|
}
|
|
34
|
+
/** Point DID resolution at a PLC directory. `runBackfill` does this too; call it
|
|
35
|
+
* at boot so resolvers that run before the first backfill have somewhere to go. */
|
|
36
|
+
export declare function configurePlc(url: string): void;
|
|
37
|
+
/** A repo's PDS endpoint, cached for the process. For serving blobs, where a
|
|
38
|
+
* stale answer after a migration costs one broken image, not a wrong row. */
|
|
39
|
+
export declare function pdsFor(did: string): Promise<string>;
|
|
32
40
|
/**
|
|
33
41
|
* Downloads and indexes a single user's repo via `com.atproto.sync.getRepo`.
|
|
34
42
|
*
|
package/dist/backfill.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../src/backfill.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAE5E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE/D;AAGD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,6CAA6C;AAC7C,UAAU,YAAY;IACpB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAA;IACd,yEAAyE;IACzE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,wDAAwD;IACxD,MAAM,EAAE,cAAc,CAAA;CACvB;
|
|
1
|
+
{"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../src/backfill.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAE5E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAE/D;AAGD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,6CAA6C;AAC7C,UAAU,YAAY;IACpB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAA;IACd,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAA;IACd,yEAAyE;IACzE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,wDAAwD;IACxD,MAAM,EAAE,cAAc,CAAA;CACvB;AAWD;oFACoF;AACpF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAID;8EAC8E;AAC9E,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzD;AAsFD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyK/G;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAsIrE"}
|
package/dist/backfill.js
CHANGED
|
@@ -30,6 +30,22 @@ import { emit, timer } from "./logger.js";
|
|
|
30
30
|
import { validateRecord } from '@bigmoves/lexicon';
|
|
31
31
|
import { getLexiconArray } from "./database/schema.js";
|
|
32
32
|
let plcUrl;
|
|
33
|
+
/** Point DID resolution at a PLC directory. `runBackfill` does this too; call it
|
|
34
|
+
* at boot so resolvers that run before the first backfill have somewhere to go. */
|
|
35
|
+
export function configurePlc(url) {
|
|
36
|
+
plcUrl = url;
|
|
37
|
+
}
|
|
38
|
+
const pdsCache = new Map();
|
|
39
|
+
/** A repo's PDS endpoint, cached for the process. For serving blobs, where a
|
|
40
|
+
* stale answer after a migration costs one broken image, not a wrong row. */
|
|
41
|
+
export async function pdsFor(did) {
|
|
42
|
+
const cached = pdsCache.get(did);
|
|
43
|
+
if (cached)
|
|
44
|
+
return cached;
|
|
45
|
+
const { pds } = await resolvePds(did);
|
|
46
|
+
pdsCache.set(did, pds);
|
|
47
|
+
return pds;
|
|
48
|
+
}
|
|
33
49
|
/**
|
|
34
50
|
* Resolves a DID to its PDS endpoint and handle by fetching the DID document.
|
|
35
51
|
*
|
|
@@ -376,26 +392,30 @@ export async function runBackfill(opts) {
|
|
|
376
392
|
}
|
|
377
393
|
}
|
|
378
394
|
else if (config.fullNetwork) {
|
|
379
|
-
for
|
|
380
|
-
|
|
395
|
+
for (const url of [pdsUrl, ...(opts.extraPdsUrls ?? [])]) {
|
|
396
|
+
for await (const repo of listRepos(url)) {
|
|
397
|
+
dids.add(repo.did);
|
|
398
|
+
}
|
|
381
399
|
}
|
|
382
400
|
}
|
|
383
401
|
else {
|
|
384
|
-
for (const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
catch (err) {
|
|
391
|
-
// Fall back to listRepos if listReposByCollection not supported
|
|
392
|
-
if (err.message.includes('400') || err.message.includes('401') || err.message.includes('501')) {
|
|
393
|
-
for await (const repo of listRepos(pdsUrl)) {
|
|
402
|
+
for (const url of [pdsUrl, ...(opts.extraPdsUrls ?? [])]) {
|
|
403
|
+
for (const col of signalCollections) {
|
|
404
|
+
try {
|
|
405
|
+
for await (const repo of listReposByCollection(url, col)) {
|
|
394
406
|
dids.add(repo.did);
|
|
395
407
|
}
|
|
396
|
-
break;
|
|
397
408
|
}
|
|
398
|
-
|
|
409
|
+
catch (err) {
|
|
410
|
+
// Fall back to listRepos if listReposByCollection not supported
|
|
411
|
+
if (err.message.includes('400') || err.message.includes('401') || err.message.includes('501')) {
|
|
412
|
+
for await (const repo of listRepos(url)) {
|
|
413
|
+
dids.add(repo.did);
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
throw err;
|
|
418
|
+
}
|
|
399
419
|
}
|
|
400
420
|
}
|
|
401
421
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -79,6 +79,14 @@ export interface JetstreamConfig {
|
|
|
79
79
|
}
|
|
80
80
|
export interface HatkConfig {
|
|
81
81
|
relay: string;
|
|
82
|
+
/**
|
|
83
|
+
* Additional `subscribeRepos` sources tailed alongside `relay`, each with
|
|
84
|
+
* its own cursor. For repos whose PDS is not behind the relay — a dev stack
|
|
85
|
+
* with more than one PDS, a self-hosted network — tail the PDS directly
|
|
86
|
+
* instead of standing up a relay to merge the streams. Ignored when
|
|
87
|
+
* `jetstream` is set (Jetstream is the sole source then).
|
|
88
|
+
*/
|
|
89
|
+
relays: string[];
|
|
82
90
|
/**
|
|
83
91
|
* Consume the stream from a Jetstream v2 instance instead of `relay`.
|
|
84
92
|
*
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -40,6 +40,7 @@ export async function loadConfig(configPath) {
|
|
|
40
40
|
const database = env.DATABASE || parsed.database;
|
|
41
41
|
const config = {
|
|
42
42
|
relay: env.RELAY || parsed.relay || 'ws://localhost:2583',
|
|
43
|
+
relays: env.RELAYS ? env.RELAYS.split(',').map((s) => s.trim()).filter(Boolean) : parsed.relays || [],
|
|
43
44
|
jetstream: env.JETSTREAM_URL ? { url: env.JETSTREAM_URL } : parsed.jetstream || null,
|
|
44
45
|
plc: env.DID_PLC_URL || parsed.plc || 'https://plc.directory',
|
|
45
46
|
port: parseInt(env.PORT || '') || parsed.port || 3000,
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import type { SearchPort } from '../ports.ts';
|
|
2
2
|
import type { DatabasePort } from '../ports.ts';
|
|
3
|
-
/**
|
|
4
|
-
* SQLite FTS5-based search port with incremental updates.
|
|
5
|
-
*
|
|
6
|
-
* Uses external content FTS5 tables (content=shadowTable) so the FTS index
|
|
7
|
-
* references the shadow data table. Updates happen incrementally per-record
|
|
8
|
-
* instead of dropping and rebuilding the entire index.
|
|
9
|
-
*/
|
|
10
3
|
export declare class SQLiteSearchPort implements SearchPort {
|
|
11
4
|
private port;
|
|
12
5
|
constructor(port: DatabasePort);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-search.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/sqlite-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"sqlite-search.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/sqlite-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAY/C,qBAAa,gBAAiB,YAAW,UAAU;IACrC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,YAAY;IAEhC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQlD,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB5F,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAClC,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,IAAI,CAAC;IA2BV,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAKjF,cAAc;IAatB,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EAAE,EACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAWlD"}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* references the shadow data table. Updates happen incrementally per-record
|
|
6
6
|
* instead of dropping and rebuilding the entire index.
|
|
7
7
|
*/
|
|
8
|
+
/** Quote an identifier. Lexicon property names are SQL-agnostic (`group`, `order`, `from` are all legal), so every column is quoted. */
|
|
9
|
+
const q = (name) => `"${name.replace(/"/g, '""')}"`;
|
|
8
10
|
export class SQLiteSearchPort {
|
|
9
11
|
port;
|
|
10
12
|
constructor(port) {
|
|
@@ -24,19 +26,19 @@ export class SQLiteSearchPort {
|
|
|
24
26
|
await this.port.execute(`CREATE TABLE ${shadowTable} AS ${sourceQuery}`, []);
|
|
25
27
|
await this.port.execute(`CREATE UNIQUE INDEX IF NOT EXISTS ${shadowTable}_uri ON ${shadowTable}(uri)`, []);
|
|
26
28
|
// Create FTS5 virtual table with external content pointing to shadow table
|
|
27
|
-
const colList = searchColumns.join(', ');
|
|
29
|
+
const colList = searchColumns.map(q).join(', ');
|
|
28
30
|
await this.port.execute(`CREATE VIRTUAL TABLE ${shadowTable}_fts USING fts5(uri UNINDEXED, ${colList}, content=${shadowTable}, content_rowid=rowid, tokenize='porter unicode61 remove_diacritics 2')`, []);
|
|
29
31
|
// Populate FTS from shadow table
|
|
30
|
-
const selectCols = ['uri', ...searchColumns].map((c) => `COALESCE(CAST(${c} AS TEXT), '')`);
|
|
32
|
+
const selectCols = ['uri', ...searchColumns].map((c) => `COALESCE(CAST(${q(c)} AS TEXT), '')`);
|
|
31
33
|
await this.port.execute(`INSERT INTO ${shadowTable}_fts (uri, ${colList}) SELECT ${selectCols.join(', ')} FROM ${shadowTable}`, []);
|
|
32
34
|
}
|
|
33
35
|
async updateIndex(shadowTable, uri, row, searchColumns) {
|
|
34
|
-
const colList = searchColumns.join(', ');
|
|
36
|
+
const colList = searchColumns.map(q).join(', ');
|
|
35
37
|
// Remove old FTS entry if record already indexed
|
|
36
38
|
await this._deleteFromFts(shadowTable, uri, searchColumns);
|
|
37
39
|
// Upsert shadow table
|
|
38
40
|
const placeholders = searchColumns.map((_, i) => `$${i + 2}`);
|
|
39
|
-
const setClauses = searchColumns.map((c, i) => `${c} = $${i + 2}`);
|
|
41
|
+
const setClauses = searchColumns.map((c, i) => `${q(c)} = $${i + 2}`);
|
|
40
42
|
const values = [uri, ...searchColumns.map((c) => row[c] ?? null)];
|
|
41
43
|
await this.port.execute(`INSERT INTO ${shadowTable} (uri, ${colList}) VALUES ($1, ${placeholders.join(', ')}) ON CONFLICT(uri) DO UPDATE SET ${setClauses.join(', ')}`, values);
|
|
42
44
|
// Read back rowid and insert new FTS entry
|
|
@@ -52,7 +54,7 @@ export class SQLiteSearchPort {
|
|
|
52
54
|
await this.port.execute(`DELETE FROM ${shadowTable} WHERE uri = $1`, [uri]);
|
|
53
55
|
}
|
|
54
56
|
async _deleteFromFts(shadowTable, uri, searchColumns) {
|
|
55
|
-
const colList = searchColumns.join(', ');
|
|
57
|
+
const colList = searchColumns.map(q).join(', ');
|
|
56
58
|
const rows = await this.port.query(`SELECT rowid, uri, ${colList} FROM ${shadowTable} WHERE uri = $1`, [uri]);
|
|
57
59
|
if (rows.length === 0)
|
|
58
60
|
return;
|
package/dist/dev-entry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../src/dev-entry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../src/dev-entry.ts"],"names":[],"mappings":"AAyHA,eAAO,MAAM,OAAO,yCAKlB,CAAA;AAEF,yEAAyE;AACzE,wBAAsB,YAAY,kBAEjC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/dev-entry.js
CHANGED
|
@@ -14,9 +14,9 @@ import { initOAuth } from "./oauth/server.js";
|
|
|
14
14
|
import { initServer } from "./server-init.js";
|
|
15
15
|
import { createHandler, registerCoreHandlers } from "./server.js";
|
|
16
16
|
import { setPrivateCollections } from "./private-collections.js";
|
|
17
|
-
import { startIndexer } from "./indexer.js";
|
|
17
|
+
import { auxCursorKey, startAuxIndexer, startIndexer } from "./indexer.js";
|
|
18
18
|
import { getCursor } from "./database/db.js";
|
|
19
|
-
import { runBackfill } from "./backfill.js";
|
|
19
|
+
import { runBackfill, configurePlc } from "./backfill.js";
|
|
20
20
|
import { rebuildAllIndexes } from "./database/fts.js";
|
|
21
21
|
import { relayHttpUrl } from "./config.js";
|
|
22
22
|
import { validateLexicons } from '@bigmoves/lexicon';
|
|
@@ -29,6 +29,7 @@ const configPath = 'hatk.config.ts';
|
|
|
29
29
|
const configDir = dirname(resolve(configPath));
|
|
30
30
|
const config = await loadConfig(configPath);
|
|
31
31
|
configureRelay(config.relay);
|
|
32
|
+
configurePlc(config.plc);
|
|
32
33
|
configureCdn(config.cdn);
|
|
33
34
|
const lexicons = loadLexicons(resolve(configDir, 'lexicons'));
|
|
34
35
|
const lexiconErrors = validateLexicons([...lexicons.values()]);
|
|
@@ -89,9 +90,14 @@ startIndexer({
|
|
|
89
90
|
parallelism: config.backfill.parallelism,
|
|
90
91
|
ftsRebuildInterval: config.ftsRebuildInterval,
|
|
91
92
|
});
|
|
93
|
+
for (const relayUrl of config.relays) {
|
|
94
|
+
const auxCursor = ignoreSavedCursor ? null : await getCursor(auxCursorKey(relayUrl));
|
|
95
|
+
startAuxIndexer({ relayUrl, collections: collectionSet, cursor: auxCursor });
|
|
96
|
+
}
|
|
92
97
|
// Run backfill in background (no restart in dev mode)
|
|
93
98
|
runBackfill({
|
|
94
99
|
pdsUrl: relayHttpUrl(config.relay),
|
|
100
|
+
extraPdsUrls: config.relays.map(relayHttpUrl),
|
|
95
101
|
plcUrl: config.plc,
|
|
96
102
|
collections: collectionSet,
|
|
97
103
|
config: config.backfill,
|
|
@@ -114,6 +120,6 @@ export { getRenderer } from "./renderer.js";
|
|
|
114
120
|
export { callXrpc } from "./xrpc.js";
|
|
115
121
|
export { parseSessionCookie, getSessionCookieName } from "./oauth/session.js";
|
|
116
122
|
log(`[hatk] Dev server ready`);
|
|
117
|
-
log(` Relay: ${config.relay}`);
|
|
123
|
+
log(` Relay: ${config.relay}${config.relays.length ? ` (+ ${config.relays.join(', ')})` : ''}`);
|
|
118
124
|
log(` Database: ${config.database}`);
|
|
119
125
|
log(` Collections: ${collections.join(', ')}`);
|
package/dist/indexer.d.ts
CHANGED
|
@@ -81,6 +81,30 @@ interface IndexerOpts extends IndexerCoreOpts {
|
|
|
81
81
|
*/
|
|
82
82
|
export declare function configureIndexer(opts: IndexerCoreOpts): Promise<void>;
|
|
83
83
|
export declare function startIndexer(opts: IndexerOpts): Promise<WebSocket>;
|
|
84
|
+
/** Configuration for an auxiliary firehose (see {@link startAuxIndexer}). */
|
|
85
|
+
export interface AuxIndexerOpts {
|
|
86
|
+
relayUrl: string;
|
|
87
|
+
collections: Set<string>;
|
|
88
|
+
cursor?: string | null;
|
|
89
|
+
}
|
|
90
|
+
/** The `_cursor` row an auxiliary firehose persists its position to. */
|
|
91
|
+
export declare function auxCursorKey(relayUrl: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* Tail a second `subscribeRepos` alongside the primary stream.
|
|
94
|
+
*
|
|
95
|
+
* A relay is one coordinate system; a PDS tailed directly is another. Nothing
|
|
96
|
+
* below the wire cares which socket a frame arrived on — `processMessage`
|
|
97
|
+
* decodes it and `applyCommit` indexes it — so the only state an extra source
|
|
98
|
+
* needs of its own is a seq and a cursor row. Both live in this closure,
|
|
99
|
+
* keyed by URL, so an aux stream never advances (or resumes from) the primary
|
|
100
|
+
* cursor. Use this when a repo's PDS is not behind the relay being tailed —
|
|
101
|
+
* a self-hosted network, a dev stack with more than one PDS — rather than
|
|
102
|
+
* standing up a relay just to merge two streams.
|
|
103
|
+
*
|
|
104
|
+
* Must be called after {@link configureIndexer} (or {@link startIndexer}),
|
|
105
|
+
* which owns the shared indexer configuration.
|
|
106
|
+
*/
|
|
107
|
+
export declare function startAuxIndexer(opts: AuxIndexerOpts): WebSocket;
|
|
84
108
|
/**
|
|
85
109
|
* Handle a `#identity` firehose event for a DID. The `handle` field on the
|
|
86
110
|
* event is optional per the lexicon, and some emitters omit it (signalling
|
|
@@ -115,6 +139,6 @@ export declare function applyCommit(did: string, ops: CommitOp[]): void;
|
|
|
115
139
|
* for relevant collections, validates records against lexicons, and routes
|
|
116
140
|
* writes to the buffer (or pending buffer if the DID is mid-backfill).
|
|
117
141
|
*/
|
|
118
|
-
export declare function processMessage(bytes: Uint8Array, collections: Set<string
|
|
142
|
+
export declare function processMessage(bytes: Uint8Array, collections: Set<string>, onSeq?: (seq: number) => void): void;
|
|
119
143
|
export {};
|
|
120
144
|
//# sourceMappingURL=indexer.d.ts.map
|
package/dist/indexer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AA8BA,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC7B;AA0JD,6GAA6G;AAC7G,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAE1C;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAStD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAErH;AAED,+CAA+C;AAC/C,wBAAgB,yBAAyB,IAAI,IAAI,CAIhD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAMpD;AAED;;;;;;;GAOG;AACH,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxD;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA8EjF;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,oDAAoD;AACpD,UAAU,WAAY,SAAQ,eAAe;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAyBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B3E;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAkCxE;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CvG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAE3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI,CAwE9D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"indexer.d.ts","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AA8BA,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC7B;AA0JD,6GAA6G;AAC7G,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAE1C;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAStD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAErH;AAED,+CAA+C;AAC/C,wBAAgB,yBAAyB,IAAI,IAAI,CAIhD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAMpD;AAED;;;;;;;GAOG;AACH,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxD;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA8EjF;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,oDAAoD;AACpD,UAAU,WAAY,SAAQ,eAAe;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAyBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B3E;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAkCxE;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAgD/D;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CvG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAE3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI,CAwE9D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAc,GACrC,IAAI,CAoDN"}
|
package/dist/indexer.js
CHANGED
|
@@ -387,6 +387,74 @@ export async function startIndexer(opts) {
|
|
|
387
387
|
});
|
|
388
388
|
return ws;
|
|
389
389
|
}
|
|
390
|
+
/** The `_cursor` row an auxiliary firehose persists its position to. */
|
|
391
|
+
export function auxCursorKey(relayUrl) {
|
|
392
|
+
return `relay:${relayUrl}`;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Tail a second `subscribeRepos` alongside the primary stream.
|
|
396
|
+
*
|
|
397
|
+
* A relay is one coordinate system; a PDS tailed directly is another. Nothing
|
|
398
|
+
* below the wire cares which socket a frame arrived on — `processMessage`
|
|
399
|
+
* decodes it and `applyCommit` indexes it — so the only state an extra source
|
|
400
|
+
* needs of its own is a seq and a cursor row. Both live in this closure,
|
|
401
|
+
* keyed by URL, so an aux stream never advances (or resumes from) the primary
|
|
402
|
+
* cursor. Use this when a repo's PDS is not behind the relay being tailed —
|
|
403
|
+
* a self-hosted network, a dev stack with more than one PDS — rather than
|
|
404
|
+
* standing up a relay just to merge two streams.
|
|
405
|
+
*
|
|
406
|
+
* Must be called after {@link configureIndexer} (or {@link startIndexer}),
|
|
407
|
+
* which owns the shared indexer configuration.
|
|
408
|
+
*/
|
|
409
|
+
export function startAuxIndexer(opts) {
|
|
410
|
+
const { relayUrl, collections } = opts;
|
|
411
|
+
const cursorKey = auxCursorKey(relayUrl);
|
|
412
|
+
let seq = null;
|
|
413
|
+
let persistedSeq = null;
|
|
414
|
+
const checkpoint = async () => {
|
|
415
|
+
if (seq === null || seq === persistedSeq)
|
|
416
|
+
return;
|
|
417
|
+
const s = seq;
|
|
418
|
+
try {
|
|
419
|
+
await setCursor(cursorKey, String(s));
|
|
420
|
+
persistedSeq = s;
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
emit('indexer', 'cursor_checkpoint_error', { source: relayUrl, cursor_seq: s, error: err.message });
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
const timer = setInterval(() => void checkpoint(), CURSOR_CHECKPOINT_INTERVAL_MS);
|
|
427
|
+
timer.unref?.();
|
|
428
|
+
const connect = (cursor) => {
|
|
429
|
+
let wsUrl = `${relayUrl}/xrpc/com.atproto.sync.subscribeRepos`;
|
|
430
|
+
if (cursor) {
|
|
431
|
+
wsUrl += `?cursor=${cursor}`;
|
|
432
|
+
log(`[indexer:aux] Resuming ${relayUrl} from cursor ${cursor}`);
|
|
433
|
+
}
|
|
434
|
+
log(`[indexer:aux] Connecting to ${relayUrl}...`);
|
|
435
|
+
const ws = new WebSocket(wsUrl);
|
|
436
|
+
ws.binaryType = 'arraybuffer';
|
|
437
|
+
ws.addEventListener('message', (event) => {
|
|
438
|
+
try {
|
|
439
|
+
if (!(event.data instanceof ArrayBuffer))
|
|
440
|
+
return;
|
|
441
|
+
processMessage(new Uint8Array(event.data), collections, (s) => {
|
|
442
|
+
seq = s;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
emit('indexer', 'decode_error', { source: relayUrl, error: err instanceof Error ? err.message : String(err) });
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
ws.addEventListener('open', () => log(`[indexer:aux] Connected to ${relayUrl}`));
|
|
450
|
+
ws.addEventListener('close', () => {
|
|
451
|
+
log(`[indexer:aux] Disconnected from ${relayUrl}, reconnecting in 3s...`);
|
|
452
|
+
setTimeout(() => connect(resumeCursor(seq, opts.cursor)), 3000);
|
|
453
|
+
});
|
|
454
|
+
return ws;
|
|
455
|
+
};
|
|
456
|
+
return connect(opts.cursor);
|
|
457
|
+
}
|
|
390
458
|
/**
|
|
391
459
|
* Handle a `#identity` firehose event for a DID. The `handle` field on the
|
|
392
460
|
* event is optional per the lexicon, and some emitters omit it (signalling
|
|
@@ -535,7 +603,7 @@ export function applyCommit(did, ops) {
|
|
|
535
603
|
* for relevant collections, validates records against lexicons, and routes
|
|
536
604
|
* writes to the buffer (or pending buffer if the DID is mid-backfill).
|
|
537
605
|
*/
|
|
538
|
-
export function processMessage(bytes, collections) {
|
|
606
|
+
export function processMessage(bytes, collections, onSeq = noteSeq) {
|
|
539
607
|
const header = cborDecode(bytes, 0);
|
|
540
608
|
const body = cborDecode(bytes, header.offset);
|
|
541
609
|
// Handle identity events (handle changes). Fire-and-forget — keeps
|
|
@@ -553,7 +621,7 @@ export function processMessage(bytes, collections) {
|
|
|
553
621
|
return;
|
|
554
622
|
// Track sequence number for cursor
|
|
555
623
|
if (body.value.seq)
|
|
556
|
-
|
|
624
|
+
onSeq(body.value.seq);
|
|
557
625
|
const did = body.value.repo;
|
|
558
626
|
if (!did)
|
|
559
627
|
return;
|
package/dist/main.js
CHANGED
|
@@ -22,7 +22,7 @@ import { initFeeds, listFeeds } from "./feeds.js";
|
|
|
22
22
|
import { initXrpc, listXrpc, configureRelay, configureCdn, configureOAuth, callXrpc } from "./xrpc.js";
|
|
23
23
|
import { initOpengraph } from "./opengraph.js";
|
|
24
24
|
import { initLabels, getLabelDefinitions } from "./labels.js";
|
|
25
|
-
import { startIndexer } from "./indexer.js";
|
|
25
|
+
import { auxCursorKey, startAuxIndexer, startIndexer } from "./indexer.js";
|
|
26
26
|
import { startJetstreamIndexer } from "./jetstream.js";
|
|
27
27
|
import { rebuildAllIndexes } from "./database/fts.js";
|
|
28
28
|
import { createHandler, registerCoreHandlers } from "./server.js";
|
|
@@ -30,7 +30,7 @@ import { setPrivateCollections } from "./private-collections.js";
|
|
|
30
30
|
import { serve } from "./adapter.js";
|
|
31
31
|
import { validateLexicons } from '@bigmoves/lexicon';
|
|
32
32
|
import { relayHttpUrl } from "./config.js";
|
|
33
|
-
import { runBackfill } from "./backfill.js";
|
|
33
|
+
import { runBackfill, configurePlc } from "./backfill.js";
|
|
34
34
|
import { initOAuth } from "./oauth/server.js";
|
|
35
35
|
import { parseSessionCookie, getSessionCookieName } from "./oauth/session.js";
|
|
36
36
|
import { loadOnLoginHook } from "./hooks.js";
|
|
@@ -43,6 +43,7 @@ registerHatkResolveHook();
|
|
|
43
43
|
// 1. Load config
|
|
44
44
|
const config = await loadConfig(configPath);
|
|
45
45
|
configureRelay(config.relay);
|
|
46
|
+
configurePlc(config.plc);
|
|
46
47
|
configureCdn(config.cdn);
|
|
47
48
|
// 2. Load lexicons, validate schemas, and discover collections
|
|
48
49
|
const lexicons = loadLexicons(resolve(configDir, 'lexicons'));
|
|
@@ -150,6 +151,7 @@ if (config.push) {
|
|
|
150
151
|
const collectionSet = new Set(collections);
|
|
151
152
|
const backfillOpts = {
|
|
152
153
|
pdsUrl: relayHttpUrl(config.relay),
|
|
154
|
+
extraPdsUrls: config.relays.map(relayHttpUrl),
|
|
153
155
|
plcUrl: config.plc,
|
|
154
156
|
collections: collectionSet,
|
|
155
157
|
config: config.backfill,
|
|
@@ -227,6 +229,10 @@ if (config.jetstream) {
|
|
|
227
229
|
else {
|
|
228
230
|
const cursor = ignoreSavedCursor ? null : await getCursor('relay');
|
|
229
231
|
startIndexer({ ...indexerCore, relayUrl: config.relay, cursor });
|
|
232
|
+
for (const relayUrl of config.relays) {
|
|
233
|
+
const auxCursor = ignoreSavedCursor ? null : await getCursor(auxCursorKey(relayUrl));
|
|
234
|
+
startAuxIndexer({ relayUrl, collections: collectionSet, cursor: auxCursor });
|
|
235
|
+
}
|
|
230
236
|
}
|
|
231
237
|
// 7. Run backfill in background
|
|
232
238
|
runBackfillAndRestart();
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAsEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA4B9C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAqM3F;AAgDD,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,CA2iC5F;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"}
|
package/dist/server.js
CHANGED
|
@@ -5,7 +5,8 @@ import { join, extname } from 'node:path';
|
|
|
5
5
|
import { gzipSync } from 'node:zlib';
|
|
6
6
|
import { queryRecords, getRecordByUri, searchRecords, getSchema, reshapeRow, setRepoStatus, getRepoStatus, getRepoRetryInfo, queryLabelsForUris, insertLabels, searchAccounts, listReposPaginated, getCollectionCounts, getRepoStatusCounts, getDatabaseSize, deleteLabels, getRecentRecords, listActiveRepoDids, removeRepo, getRepoHandle, getPreferences, putPreference, insertReport, queryReports, resolveReport, getOpenReportCount, } from "./database/db.js";
|
|
7
7
|
import { executeFeed, listFeeds } from "./feeds.js";
|
|
8
|
-
import { executeXrpc, InvalidRequestError, NotFoundError, registerCoreXrpcHandler } from "./xrpc.js";
|
|
8
|
+
import { executeXrpc, InvalidRequestError, NotFoundError, registerCoreXrpcHandler, isLocalRelay } from "./xrpc.js";
|
|
9
|
+
import { pdsFor } from "./backfill.js";
|
|
9
10
|
import { resolveRecords } from "./hydrate.js";
|
|
10
11
|
import { handleOpengraphRequest, buildOgMeta } from "./opengraph.js";
|
|
11
12
|
import { getLabelDefinitions, rescanLabels } from "./labels.js";
|
|
@@ -1188,6 +1189,31 @@ export function createHandler(config) {
|
|
|
1188
1189
|
throw err;
|
|
1189
1190
|
}
|
|
1190
1191
|
}
|
|
1192
|
+
// GET /blob/:did/:cid — dev-only image proxy (see blobUrl in xrpc.ts).
|
|
1193
|
+
// A PDS serves getBlob as an attachment with nosniff, which a browser
|
|
1194
|
+
// refuses to render in <img>; re-serving it from here drops that and
|
|
1195
|
+
// lets one origin cover repos on any number of local PDSes.
|
|
1196
|
+
if (url.pathname.startsWith('/blob/') && isLocalRelay()) {
|
|
1197
|
+
const [did, cid] = url.pathname.slice('/blob/'.length).split('/');
|
|
1198
|
+
if (!did || !cid)
|
|
1199
|
+
return withCors(jsonError(400, 'Expected /blob/:did/:cid', acceptEncoding));
|
|
1200
|
+
try {
|
|
1201
|
+
const pds = await pdsFor(did);
|
|
1202
|
+
const upstream = await fetch(`${pds}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`);
|
|
1203
|
+
if (!upstream.ok)
|
|
1204
|
+
return withCors(jsonError(upstream.status, 'Blob not found', acceptEncoding));
|
|
1205
|
+
return withCors(new Response(upstream.body, {
|
|
1206
|
+
status: 200,
|
|
1207
|
+
headers: {
|
|
1208
|
+
'content-type': upstream.headers.get('content-type') || 'application/octet-stream',
|
|
1209
|
+
'cache-control': 'public, max-age=3600',
|
|
1210
|
+
},
|
|
1211
|
+
}));
|
|
1212
|
+
}
|
|
1213
|
+
catch (err) {
|
|
1214
|
+
return withCors(jsonError(502, `Blob fetch failed: ${err.message}`, acceptEncoding));
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1191
1217
|
// GET /robots.txt — serve from user's public dir or fall back to hatk default
|
|
1192
1218
|
if (url.pathname === '/robots.txt') {
|
|
1193
1219
|
const userRobots = publicDir ? join(publicDir, 'robots.txt') : null;
|
package/dist/xrpc.d.ts
CHANGED
|
@@ -82,6 +82,8 @@ export interface XrpcContext<P = Record<string, string>, Records extends Record<
|
|
|
82
82
|
pds: (nsid: string, options?: PdsXrpcOptions) => Promise<Record<string, unknown>>;
|
|
83
83
|
}
|
|
84
84
|
/** Set the relay URL used for blob URL generation. Called once during boot. */
|
|
85
|
+
/** True when the relay is a loopback URL — a dev PDS rather than the network. */
|
|
86
|
+
export declare function isLocalRelay(): boolean;
|
|
85
87
|
export declare function configureRelay(relay: string): void;
|
|
86
88
|
/** Set the CDN config for imgproxy URL signing. Called once during boot. */
|
|
87
89
|
export declare function configureCdn(cdn: CdnConfig | null): void;
|
package/dist/xrpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,CAAA;AAI5B,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,QAExD;AAED,gFAAgF;AAChF,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,MAAM,SAAM;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;gBACN,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AACD,0DAA0D;AAC1D,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,MAAM,SAAM;gBACA,OAAO,SAAc;CAGlC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW,CAC1B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzD,CAAC,GAAG,OAAO,CACX,SAAQ,WAAW;IACnB,EAAE,EAAE;QACF,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;QAC9D,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACxD,CAAA;IACD,MAAM,EAAE,CAAC,CAAA;IACT,KAAK,EAAE,CAAC,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAC7D,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACzE,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9C,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7D,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,OAAO,EACvC,UAAU,EAAE,CAAC,EACb,CAAC,EAAE,MAAM,EACT,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KACxD,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,OAAO,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC3D,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACjF,YAAY,EAAE,CACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACrB,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,SAAS,EAAE,CACT,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,YAAY,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,WAAW,EAAE,CACX,MAAM,EAAE,KAAK,CAAC;QACZ,KAAK,EAAE,MAAM,CAAA;QACb,UAAU,EAAE,MAAM,CAAA;QAClB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAChC,CAAC,KACC,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAA;IAChF;;;;OAIG;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAClF;AAiBD,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,QAE3C;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,QAUjD;AAWD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAiB,GAAG,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
|
package/dist/xrpc.js
CHANGED
|
@@ -58,6 +58,16 @@ export class NotFoundError extends InvalidRequestError {
|
|
|
58
58
|
let _relayUrl = '';
|
|
59
59
|
let _cdn = null;
|
|
60
60
|
/** Set the relay URL used for blob URL generation. Called once during boot. */
|
|
61
|
+
/** True when the relay is a loopback URL — a dev PDS rather than the network. */
|
|
62
|
+
export function isLocalRelay() {
|
|
63
|
+
try {
|
|
64
|
+
const host = new URL(_relayUrl).hostname;
|
|
65
|
+
return host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1' || host.endsWith('.localhost');
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
61
71
|
export function configureRelay(relay) {
|
|
62
72
|
_relayUrl = relay;
|
|
63
73
|
}
|
|
@@ -92,8 +102,11 @@ export function blobUrl(did, ref, preset = 'avatar') {
|
|
|
92
102
|
const p = typeof ref === 'string' ? JSON.parse(ref) : ref;
|
|
93
103
|
if (!p?.ref?.$link)
|
|
94
104
|
return undefined;
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
// A local network has no image CDN. Serve blobs through the dev proxy
|
|
106
|
+
// (see /blob/ in server.ts), which fetches from each repo's own PDS — on a
|
|
107
|
+
// multi-PDS dev stack the repos are not all behind the relay being tailed.
|
|
108
|
+
if (isLocalRelay()) {
|
|
109
|
+
return `/blob/${did}/${p.ref.$link}`;
|
|
97
110
|
}
|
|
98
111
|
if (_cdn) {
|
|
99
112
|
const path = `/${preset}/plain/${did}/${p.ref.$link}`;
|