@hatk/hatk 0.0.1-alpha.6 → 0.0.1-alpha.60
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 +78 -31
- 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 +112 -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 +22 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +123 -32
- 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 +137 -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/setup.d.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
+
import type { BulkInserter } from './database/ports.ts';
|
|
2
|
+
/** Context passed to each setup script's handler function. */
|
|
1
3
|
export interface SetupContext {
|
|
2
4
|
db: {
|
|
3
5
|
query: (sql: string, params?: any[]) => Promise<any[]>;
|
|
4
|
-
run: (sql: string,
|
|
6
|
+
run: (sql: string, params?: any[]) => Promise<void>;
|
|
7
|
+
runBatch: (operations: Array<{
|
|
8
|
+
sql: string;
|
|
9
|
+
params: any[];
|
|
10
|
+
}>) => Promise<void>;
|
|
11
|
+
createBulkInserter: (table: string, columns: string[], options?: {
|
|
12
|
+
onConflict?: 'ignore' | 'replace';
|
|
13
|
+
batchSize?: number;
|
|
14
|
+
}) => Promise<BulkInserter>;
|
|
5
15
|
};
|
|
6
16
|
}
|
|
17
|
+
export type SetupHandler = (ctx: SetupContext) => Promise<void>;
|
|
18
|
+
export declare function defineSetup(handler: SetupHandler): {
|
|
19
|
+
__type: "setup";
|
|
20
|
+
handler: SetupHandler;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Run all setup scripts in the given directory on server boot.
|
|
24
|
+
*
|
|
25
|
+
* Each script should export a default handler function (or `{ handler }`) that
|
|
26
|
+
* receives a {@link SetupContext} with database access. Scripts run in sorted
|
|
27
|
+
* filename order — prefix with numbers (e.g. `01-create-tables.ts`) to control
|
|
28
|
+
* execution order. Files starting with `_` are ignored.
|
|
29
|
+
*
|
|
30
|
+
* @param setupDir - Absolute path to the `setup/` directory
|
|
31
|
+
*/
|
|
7
32
|
export declare function initSetup(setupDir: string): Promise<void>;
|
|
33
|
+
/** Run a single setup handler with a SetupContext. */
|
|
34
|
+
export declare function runSetupHandler(name: string, handler: SetupHandler): Promise<void>;
|
|
8
35
|
//# sourceMappingURL=setup.d.ts.map
|
package/dist/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE;QACF,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,QAAQ,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,GAAG,EAAE,CAAA;SAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC9E,kBAAkB,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,KAChE,OAAO,CAAC,YAAY,CAAC,CAAA;KAC3B,CAAA;CACF;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE/D,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY;;;EAEhD;AAkBD;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/D;AAED,sDAAsD;AACtD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxF"}
|
package/dist/setup.js
CHANGED
|
@@ -6,10 +6,38 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Setup scripts that run once on server boot for initializing custom tables,
|
|
11
|
+
* views, or other database state.
|
|
12
|
+
*
|
|
13
|
+
* Place scripts in the `setup/` directory. Each module default-exports a handler
|
|
14
|
+
* function (or `{ handler }`) that receives a {@link SetupContext} with database
|
|
15
|
+
* access. Scripts run in sorted filename order — prefix with numbers to control
|
|
16
|
+
* execution order. Files starting with `_` are ignored.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // setup/01-leaderboard.ts
|
|
21
|
+
* import type { SetupContext } from '@hatk/hatk/setup'
|
|
22
|
+
*
|
|
23
|
+
* export default async function (ctx: SetupContext) {
|
|
24
|
+
* await ctx.db.run(`
|
|
25
|
+
* CREATE TABLE IF NOT EXISTS leaderboard (
|
|
26
|
+
* did TEXT PRIMARY KEY,
|
|
27
|
+
* score INTEGER DEFAULT 0
|
|
28
|
+
* )
|
|
29
|
+
* `)
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
9
33
|
import { resolve, relative } from 'node:path';
|
|
10
34
|
import { readdirSync, statSync } from 'node:fs';
|
|
11
35
|
import { log } from "./logger.js";
|
|
12
|
-
import { querySQL, runSQL } from "./db.js";
|
|
36
|
+
import { querySQL, runSQL, runBatch, createBulkInserterSQL } from "./database/db.js";
|
|
37
|
+
export function defineSetup(handler) {
|
|
38
|
+
return { __type: 'setup', handler };
|
|
39
|
+
}
|
|
40
|
+
/** Recursively collect .ts/.js files in a directory, skipping files prefixed with `_`. */
|
|
13
41
|
function walkDir(dir) {
|
|
14
42
|
const results = [];
|
|
15
43
|
try {
|
|
@@ -26,23 +54,42 @@ function walkDir(dir) {
|
|
|
26
54
|
catch { }
|
|
27
55
|
return results.sort();
|
|
28
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Run all setup scripts in the given directory on server boot.
|
|
59
|
+
*
|
|
60
|
+
* Each script should export a default handler function (or `{ handler }`) that
|
|
61
|
+
* receives a {@link SetupContext} with database access. Scripts run in sorted
|
|
62
|
+
* filename order — prefix with numbers (e.g. `01-create-tables.ts`) to control
|
|
63
|
+
* execution order. Files starting with `_` are ignored.
|
|
64
|
+
*
|
|
65
|
+
* @param setupDir - Absolute path to the `setup/` directory
|
|
66
|
+
*/
|
|
29
67
|
export async function initSetup(setupDir) {
|
|
30
68
|
const files = walkDir(setupDir);
|
|
31
69
|
if (files.length === 0)
|
|
32
70
|
return;
|
|
33
71
|
for (const scriptPath of files) {
|
|
34
72
|
const name = relative(setupDir, scriptPath).replace(/\.(ts|js)$/, '');
|
|
35
|
-
const mod = await import(__rewriteRelativeImportExtension(scriptPath));
|
|
73
|
+
const mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ `${scriptPath}?t=${Date.now()}`));
|
|
36
74
|
const handler = mod.default?.handler || mod.default;
|
|
37
75
|
if (typeof handler !== 'function') {
|
|
38
76
|
console.warn(`[setup] ${name}: no handler function found, skipping`);
|
|
39
77
|
continue;
|
|
40
78
|
}
|
|
41
79
|
const ctx = {
|
|
42
|
-
db: { query: querySQL, run: runSQL },
|
|
80
|
+
db: { query: querySQL, run: runSQL, runBatch, createBulkInserter: createBulkInserterSQL },
|
|
43
81
|
};
|
|
44
82
|
log(`[setup] running: ${name}`);
|
|
45
83
|
await handler(ctx);
|
|
46
84
|
log(`[setup] done: ${name}`);
|
|
47
85
|
}
|
|
48
86
|
}
|
|
87
|
+
/** Run a single setup handler with a SetupContext. */
|
|
88
|
+
export async function runSetupHandler(name, handler) {
|
|
89
|
+
const ctx = {
|
|
90
|
+
db: { query: querySQL, run: runSQL, runBatch, createBulkInserter: createBulkInserterSQL },
|
|
91
|
+
};
|
|
92
|
+
log(`[setup] running: ${name}`);
|
|
93
|
+
await handler(ctx);
|
|
94
|
+
log(`[setup] done: ${name}`);
|
|
95
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineFeed } from '$hatk'
|
|
2
|
+
|
|
3
|
+
export default defineFeed({
|
|
4
|
+
collection: 'your.collection.here',
|
|
5
|
+
label: '{{Name}}',
|
|
6
|
+
|
|
7
|
+
async generate(ctx) {
|
|
8
|
+
const { rows, cursor } = await ctx.paginate<{ uri: string }>(
|
|
9
|
+
`SELECT uri, cid, indexed_at FROM "your.collection.here"`,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
return ctx.ok({ uris: rows.map((r) => r.uri), cursor })
|
|
13
|
+
},
|
|
14
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineLabel } from '$hatk'
|
|
2
|
+
|
|
3
|
+
export default defineLabel({
|
|
4
|
+
definition: {
|
|
5
|
+
identifier: '{{name}}',
|
|
6
|
+
severity: 'inform',
|
|
7
|
+
blurs: 'none',
|
|
8
|
+
defaultSetting: 'warn',
|
|
9
|
+
locales: [{ lang: 'en', name: '{{Name}}', description: 'Description here' }],
|
|
10
|
+
},
|
|
11
|
+
async evaluate(ctx) {
|
|
12
|
+
// Return array of label identifiers to apply, or empty array
|
|
13
|
+
return []
|
|
14
|
+
},
|
|
15
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { OpengraphContext, OpengraphResult } from '@hatk/hatk/opengraph'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
path: '/og/{{name}}/:id',
|
|
5
|
+
async generate(ctx: OpengraphContext): Promise<OpengraphResult> {
|
|
6
|
+
const { db, params } = ctx
|
|
7
|
+
return {
|
|
8
|
+
element: {
|
|
9
|
+
type: 'div',
|
|
10
|
+
props: {
|
|
11
|
+
style: { display: 'flex', width: '100%', height: '100%', background: '#080b12', color: 'white', alignItems: 'center', justifyContent: 'center' },
|
|
12
|
+
children: params.id,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { seed } from '$hatk'
|
|
2
|
+
|
|
3
|
+
const { createAccount, createRecord } = seed()
|
|
4
|
+
|
|
5
|
+
const alice = await createAccount('alice.test')
|
|
6
|
+
|
|
7
|
+
// await createRecord(alice, 'your.collection.here', {
|
|
8
|
+
// field: 'value',
|
|
9
|
+
// }, { rkey: 'my-record' })
|
|
10
|
+
|
|
11
|
+
console.log('\n[seed] Done!')
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { createTestContext } from '@hatk/hatk/test'
|
|
3
|
+
|
|
4
|
+
let ctx: Awaited<ReturnType<typeof createTestContext>>
|
|
5
|
+
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
ctx = await createTestContext()
|
|
8
|
+
await ctx.loadFixtures()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterAll(async () => ctx?.close())
|
|
12
|
+
|
|
13
|
+
describe('{{name}} feed', () => {
|
|
14
|
+
test('returns results', async () => {
|
|
15
|
+
const feed = ctx.loadFeed('{{name}}')
|
|
16
|
+
const result = await feed.generate(ctx.feedContext({ limit: 10 }))
|
|
17
|
+
expect(result).toBeDefined()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { createTestContext } from '@hatk/hatk/test'
|
|
3
|
+
|
|
4
|
+
let ctx: Awaited<ReturnType<typeof createTestContext>>
|
|
5
|
+
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
ctx = await createTestContext()
|
|
8
|
+
await ctx.loadFixtures()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterAll(async () => ctx?.close())
|
|
12
|
+
|
|
13
|
+
describe('{{name}}', () => {
|
|
14
|
+
test('returns response', async () => {
|
|
15
|
+
const handler = ctx.loadXrpc('{{name}}')
|
|
16
|
+
const result = await handler.handler({ params: {} })
|
|
17
|
+
expect(result).toBeDefined()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { defineQuery } from '$hatk'
|
|
2
|
+
|
|
3
|
+
export default defineQuery('{{name}}', async (ctx) => {
|
|
4
|
+
const { ok, db, params, packCursor, unpackCursor } = ctx
|
|
5
|
+
const limit = params.limit ?? 30
|
|
6
|
+
const cursor = params.cursor
|
|
7
|
+
|
|
8
|
+
const conditions: string[] = []
|
|
9
|
+
const sqlParams: (string | number)[] = []
|
|
10
|
+
let paramIdx = 1
|
|
11
|
+
|
|
12
|
+
if (cursor) {
|
|
13
|
+
const parsed = unpackCursor(cursor)
|
|
14
|
+
if (parsed) {
|
|
15
|
+
conditions.push(`(s.indexed_at < $${paramIdx} OR (s.indexed_at = $${paramIdx + 1} AND s.cid < $${paramIdx + 2}))`)
|
|
16
|
+
sqlParams.push(parsed.primary, parsed.primary, parsed.cid)
|
|
17
|
+
paramIdx += 3
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const where = conditions.length ? 'WHERE ' + conditions.join(' AND ') : ''
|
|
22
|
+
|
|
23
|
+
const rows = (await db.query(
|
|
24
|
+
`SELECT s.* FROM "your.collection.here" s ${where} ORDER BY s.indexed_at DESC, s.cid DESC LIMIT $${paramIdx}`,
|
|
25
|
+
sqlParams.concat([limit + 1]),
|
|
26
|
+
)) as {
|
|
27
|
+
uri: string
|
|
28
|
+
cid: string
|
|
29
|
+
did: string
|
|
30
|
+
indexed_at: string
|
|
31
|
+
}[]
|
|
32
|
+
|
|
33
|
+
const hasMore = rows.length > limit
|
|
34
|
+
if (hasMore) rows.pop()
|
|
35
|
+
const lastRow = rows[rows.length - 1]
|
|
36
|
+
|
|
37
|
+
return ok({
|
|
38
|
+
items: rows,
|
|
39
|
+
cursor: hasMore && lastRow ? packCursor(lastRow.indexed_at, lastRow.cid) : undefined,
|
|
40
|
+
})
|
|
41
|
+
})
|
package/dist/test.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { FeedContext } from './feeds.ts';
|
|
|
4
4
|
export interface TestContext {
|
|
5
5
|
db: {
|
|
6
6
|
query: (sql: string, params?: any[]) => Promise<any[]>;
|
|
7
|
-
run: (sql: string,
|
|
7
|
+
run: (sql: string, params?: any[]) => Promise<void>;
|
|
8
8
|
};
|
|
9
9
|
loadFixtures: (dir?: string) => Promise<void>;
|
|
10
10
|
loadFeed: (name: string) => {
|
package/dist/test.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAkBzD,OAAO,EAAE,IAAI,IAAI,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE;QACF,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACpD,CAAA;IACD,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KAAE,CAAA;IAC5E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KAAE,CAAA;IACnE,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE;QACnB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;QAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAChC,KAAK,WAAW,CAAA;IACjB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAA;IACpC,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC7E,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,KAAK,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA;IAC/D,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAClE;AAYD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,WAAW,CAAC,CA2K9D;AA8BD,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAgD3D"}
|
package/dist/test.js
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import { resolve, dirname } from 'node:path';
|
|
2
2
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { registerHatkResolveHook } from "./resolve-hatk.js";
|
|
3
4
|
import YAML from 'yaml';
|
|
4
5
|
import { loadConfig } from "./config.js";
|
|
5
|
-
import { loadLexicons, storeLexicons, discoverCollections, generateTableSchema, generateCreateTableSQL, } from "./schema.js";
|
|
6
|
-
import { initDatabase, querySQL, runSQL, insertRecord, closeDatabase } from "./db.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import { loadLexicons, storeLexicons, discoverCollections, generateTableSchema, generateCreateTableSQL, } from "./database/schema.js";
|
|
7
|
+
import { initDatabase, querySQL, runSQL, insertRecord, closeDatabase } from "./database/db.js";
|
|
8
|
+
import { createAdapter } from "./database/adapter-factory.js";
|
|
9
|
+
import { SQLITE_DIALECT } from "./database/dialect.js";
|
|
10
|
+
import { setSearchPort } from "./database/fts.js";
|
|
11
|
+
import { executeFeed, listFeeds, createPaginate } from "./feeds.js";
|
|
12
|
+
import { executeXrpc, listXrpc, configureRelay, configureCdn } from "./xrpc.js";
|
|
13
|
+
import { initServer } from "./server-init.js";
|
|
11
14
|
import { discoverViews } from "./views.js";
|
|
12
|
-
import { loadOnLoginHook } from "./oauth/hooks.js";
|
|
13
15
|
import { validateLexicons } from '@bigmoves/lexicon';
|
|
14
|
-
import { packCursor, unpackCursor, isTakendownDid, filterTakendownDids } from "./db.js";
|
|
16
|
+
import { packCursor, unpackCursor, isTakendownDid, filterTakendownDids } from "./database/db.js";
|
|
15
17
|
import { seed as createSeedHelpers } from "./seed.js";
|
|
16
18
|
/**
|
|
17
|
-
* Find the project's config.
|
|
18
|
-
* Returns the resolved config path, or falls back to 'config.
|
|
19
|
+
* Find the project's hatk.config.ts by walking up from cwd.
|
|
20
|
+
* Returns the resolved config path, or falls back to 'hatk.config.ts'.
|
|
19
21
|
*/
|
|
20
22
|
function findConfigPath() {
|
|
21
23
|
const explicit = process.env.APPVIEW_CONFIG;
|
|
22
24
|
if (explicit)
|
|
23
25
|
return resolve(explicit);
|
|
24
|
-
return resolve('config.
|
|
26
|
+
return resolve('hatk.config.ts');
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Boot an in-memory hatk context for unit tests.
|
|
@@ -33,10 +35,12 @@ function findConfigPath() {
|
|
|
33
35
|
* but it will NOT work with --pool=threads (multiple tests sharing a process).
|
|
34
36
|
*/
|
|
35
37
|
export async function createTestContext() {
|
|
38
|
+
registerHatkResolveHook();
|
|
36
39
|
const configPath = findConfigPath();
|
|
37
|
-
const config = loadConfig(configPath);
|
|
40
|
+
const config = await loadConfig(configPath);
|
|
38
41
|
const configDir = dirname(resolve(configPath));
|
|
39
42
|
configureRelay(config.relay);
|
|
43
|
+
configureCdn(config.cdn);
|
|
40
44
|
// Load and validate lexicons
|
|
41
45
|
const lexicons = loadLexicons(resolve(configDir, 'lexicons'));
|
|
42
46
|
const lexiconErrors = validateLexicons([...lexicons.values()]);
|
|
@@ -56,23 +60,16 @@ export async function createTestContext() {
|
|
|
56
60
|
continue;
|
|
57
61
|
const schema = generateTableSchema(nsid, lexicon, lexicons);
|
|
58
62
|
schemas.push(schema);
|
|
59
|
-
ddlStatements.push(generateCreateTableSQL(schema));
|
|
63
|
+
ddlStatements.push(generateCreateTableSQL(schema, SQLITE_DIALECT));
|
|
60
64
|
}
|
|
61
|
-
// In-memory
|
|
62
|
-
await
|
|
63
|
-
|
|
65
|
+
// In-memory SQLite — faster startup, no native module issues in Vite's module runner
|
|
66
|
+
const { adapter, searchPort } = await createAdapter('sqlite');
|
|
67
|
+
setSearchPort(searchPort);
|
|
68
|
+
await initDatabase(adapter, ':memory:', schemas, ddlStatements);
|
|
69
|
+
// Discover views
|
|
64
70
|
discoverViews();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
catch { }
|
|
69
|
-
// Skip setup hooks in test context — they're for server boot-time
|
|
70
|
-
// initialization (e.g. importing large datasets) and not appropriate for tests
|
|
71
|
-
// Discover feeds, xrpc, labels
|
|
72
|
-
await initFeeds(resolve(configDir, 'feeds'));
|
|
73
|
-
await initXrpc(resolve(configDir, 'xrpc'));
|
|
74
|
-
await initOpengraph(resolve(configDir, 'og'));
|
|
75
|
-
await initLabels(resolve(configDir, 'labels'));
|
|
71
|
+
// Discover feeds, xrpc, labels, hooks, og from server/ directory (skip setup scripts in tests)
|
|
72
|
+
await initServer(resolve(configDir, 'server'), { skipSetup: true });
|
|
76
73
|
return {
|
|
77
74
|
db: { query: querySQL, run: runSQL },
|
|
78
75
|
_config: config,
|
|
@@ -94,7 +91,12 @@ export async function createTestContext() {
|
|
|
94
91
|
if (Array.isArray(records)) {
|
|
95
92
|
for (const rec of records) {
|
|
96
93
|
const row = interpolateHelpers(rec);
|
|
97
|
-
await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`,
|
|
94
|
+
await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, [
|
|
95
|
+
row.did,
|
|
96
|
+
row.status || 'active',
|
|
97
|
+
row.handle || row.did.split(':').pop() + '.test',
|
|
98
|
+
new Date().toISOString(),
|
|
99
|
+
]);
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
}
|
|
@@ -119,7 +121,7 @@ export async function createTestContext() {
|
|
|
119
121
|
const row = interpolateHelpers(rec);
|
|
120
122
|
const vals = keys.map((k) => row[k]);
|
|
121
123
|
const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');
|
|
122
|
-
await runSQL(`INSERT INTO "${tableName}" (${keys.map((k) => `"${k}"`).join(', ')}) VALUES (${placeholders})`,
|
|
124
|
+
await runSQL(`INSERT INTO "${tableName}" (${keys.map((k) => `"${k}"`).join(', ')}) VALUES (${placeholders})`, vals);
|
|
123
125
|
}
|
|
124
126
|
continue;
|
|
125
127
|
}
|
|
@@ -133,7 +135,12 @@ export async function createTestContext() {
|
|
|
133
135
|
// Auto-register DID in _repos if not already present
|
|
134
136
|
if (!seenDids.has(did)) {
|
|
135
137
|
seenDids.add(did);
|
|
136
|
-
await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`,
|
|
138
|
+
await runSQL(`INSERT OR IGNORE INTO _repos (did, status, handle, backfilled_at) VALUES ($1, $2, $3, $4)`, [
|
|
139
|
+
did,
|
|
140
|
+
'active',
|
|
141
|
+
did.split(':').pop() + '.test',
|
|
142
|
+
new Date().toISOString(),
|
|
143
|
+
]);
|
|
137
144
|
}
|
|
138
145
|
await insertRecord(tableName, uri, cid, did, fields);
|
|
139
146
|
}
|
|
@@ -220,8 +227,8 @@ export async function startTestServer() {
|
|
|
220
227
|
// Import startServer — it creates the HTTP server and returns it
|
|
221
228
|
const { startServer } = await import("./server.js");
|
|
222
229
|
// Start server on port 0 (random available port)
|
|
223
|
-
const resolveViewer = (
|
|
224
|
-
const did =
|
|
230
|
+
const resolveViewer = (request) => {
|
|
231
|
+
const did = request.headers.get('x-test-viewer');
|
|
225
232
|
return typeof did === 'string' ? { did } : null;
|
|
226
233
|
};
|
|
227
234
|
const httpServer = startServer(0, ctx._collections, ctx._config.publicDir, ctx._config.oauth, ctx._config.admins, resolveViewer);
|
package/dist/views.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// 1. Inline views: defined in the record lexicon with ref: "#main" (e.g., playView)
|
|
4
4
|
// 2. Defs views: defined in a defs lexicon, associated by naming convention (e.g., profileView)
|
|
5
5
|
import { log } from "./logger.js";
|
|
6
|
-
import { getAllLexicons, getLexicon } from "./schema.js";
|
|
6
|
+
import { getAllLexicons, getLexicon } from "./database/schema.js";
|
|
7
7
|
// --- Registry ---
|
|
8
8
|
/** All views keyed by full NSID (e.g., "fm.teal.alpha.feed.play#playView") */
|
|
9
9
|
const views = new Map();
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,MAAM,EAA6C,MAAM,MAAM,CAAA;AA8D3G,wBAAgB,IAAI,CAAC,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAoOrD"}
|