@hatk/hatk 0.0.1-alpha.9 → 0.0.1-alpha.90
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 +113 -0
- package/dist/backfill.d.ts +29 -2
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +137 -51
- package/dist/car.d.ts +20 -0
- package/dist/car.d.ts.map +1 -1
- package/dist/car.js +108 -0
- package/dist/cli.js +247 -1043
- package/dist/config.d.ts +145 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -8
- 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 +16 -0
- package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
- package/dist/database/adapters/sqlite-search.js +76 -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} +68 -6
- package/dist/database/db.d.ts.map +1 -0
- package/dist/database/db.js +1775 -0
- 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} +122 -34
- 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} +16 -3
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/{schema.js → database/schema.js} +126 -44
- package/dist/dev-entry.d.ts +8 -0
- package/dist/dev-entry.d.ts.map +1 -0
- package/dist/dev-entry.js +127 -0
- package/dist/feeds.d.ts +26 -8
- package/dist/feeds.d.ts.map +1 -1
- package/dist/feeds.js +72 -16
- package/dist/hooks.d.ts +102 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +186 -0
- package/dist/hydrate.d.ts +49 -6
- package/dist/hydrate.d.ts.map +1 -1
- package/dist/hydrate.js +21 -20
- package/dist/indexer.d.ts +156 -3
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +539 -94
- package/dist/jetstream.d.ts +118 -0
- package/dist/jetstream.d.ts.map +1 -0
- package/dist/jetstream.js +225 -0
- package/dist/labels.d.ts +36 -0
- package/dist/labels.d.ts.map +1 -1
- package/dist/labels.js +71 -6
- package/dist/lex-types.d.ts +2 -0
- package/dist/lex-types.d.ts.map +1 -1
- 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 +21 -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 +198 -75
- package/dist/mst.d.ts +15 -0
- package/dist/mst.d.ts.map +1 -1
- package/dist/mst.js +13 -0
- package/dist/oauth/db.d.ts +16 -1
- package/dist/oauth/db.d.ts.map +1 -1
- package/dist/oauth/db.js +74 -19
- package/dist/oauth/discovery.d.ts +10 -0
- package/dist/oauth/discovery.d.ts.map +1 -1
- package/dist/oauth/discovery.js +54 -2
- package/dist/oauth/server.d.ts +93 -1
- package/dist/oauth/server.d.ts.map +1 -1
- package/dist/oauth/server.js +416 -39
- package/dist/oauth/service-describe.d.ts +42 -0
- package/dist/oauth/service-describe.d.ts.map +1 -0
- package/dist/oauth/service-describe.js +108 -0
- package/dist/oauth/session.d.ts +32 -0
- package/dist/oauth/session.d.ts.map +1 -0
- package/dist/oauth/session.js +131 -0
- package/dist/opengraph.d.ts +10 -0
- package/dist/opengraph.d.ts.map +1 -1
- package/dist/opengraph.js +94 -42
- package/dist/pds-proxy.d.ts +82 -0
- package/dist/pds-proxy.d.ts.map +1 -0
- package/dist/pds-proxy.js +356 -0
- package/dist/private-collections.d.ts +13 -0
- package/dist/private-collections.d.ts.map +1 -0
- package/dist/private-collections.js +27 -0
- package/dist/push.d.ts +54 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +367 -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 +34 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1021 -649
- package/dist/setup.d.ts +28 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +50 -3
- package/dist/spaces/blob.d.ts +51 -0
- package/dist/spaces/blob.d.ts.map +1 -0
- package/dist/spaces/blob.js +111 -0
- package/dist/spaces/credential.d.ts +62 -0
- package/dist/spaces/credential.d.ts.map +1 -0
- package/dist/spaces/credential.js +184 -0
- package/dist/spaces/engine.d.ts +97 -0
- package/dist/spaces/engine.d.ts.map +1 -0
- package/dist/spaces/engine.js +559 -0
- package/dist/spaces/guard.d.ts +41 -0
- package/dist/spaces/guard.d.ts.map +1 -0
- package/dist/spaces/guard.js +79 -0
- package/dist/spaces/identity.d.ts +37 -0
- package/dist/spaces/identity.d.ts.map +1 -0
- package/dist/spaces/identity.js +88 -0
- package/dist/spaces/index.d.ts +55 -0
- package/dist/spaces/index.d.ts.map +1 -0
- package/dist/spaces/index.js +133 -0
- package/dist/spaces/notify.d.ts +51 -0
- package/dist/spaces/notify.d.ts.map +1 -0
- package/dist/spaces/notify.js +121 -0
- package/dist/spaces/records.d.ts +37 -0
- package/dist/spaces/records.d.ts.map +1 -0
- package/dist/spaces/records.js +63 -0
- package/dist/spaces/store.d.ts +51 -0
- package/dist/spaces/store.d.ts.map +1 -0
- package/dist/spaces/store.js +99 -0
- package/dist/spaces/uri.d.ts +60 -0
- package/dist/spaces/uri.d.ts.map +1 -0
- package/dist/spaces/uri.js +97 -0
- package/dist/spaces/verify.d.ts +39 -0
- package/dist/spaces/verify.d.ts.map +1 -0
- package/dist/spaces/verify.js +87 -0
- package/dist/spaces/viewer.d.ts +51 -0
- package/dist/spaces/viewer.d.ts.map +1 -0
- package/dist/spaces/viewer.js +167 -0
- package/dist/spaces/visibility.d.ts +57 -0
- package/dist/spaces/visibility.d.ts.map +1 -0
- package/dist/spaces/visibility.js +81 -0
- package/dist/templates/feed.tpl +18 -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 +53 -0
- package/dist/test.d.ts +3 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +60 -36
- 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 +101 -12
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +255 -55
- package/package.json +21 -9
- package/public/admin-auth.js +271 -306
- package/public/admin.html +177 -61
- package/dist/db.d.ts.map +0 -1
- package/dist/db.js +0 -1361
- 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
|
@@ -1,42 +1,59 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { DUCKDB_DIALECT } from "./dialect.js";
|
|
3
4
|
// Convert camelCase to snake_case
|
|
4
5
|
export function toSnakeCase(str) {
|
|
5
6
|
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The columns every generated table carries before any lexicon field. A record
|
|
10
|
+
* property with one of these names — `fyi.opensocial.space` has a `uri` — would
|
|
11
|
+
* otherwise produce a CREATE TABLE with a duplicate column and fail at boot.
|
|
12
|
+
* It is stored under `record_<name>` instead; `originalName` still carries the
|
|
13
|
+
* lexicon's spelling, which is what every reshaped row is keyed by.
|
|
14
|
+
*/
|
|
15
|
+
const ENVELOPE_COLUMNS = new Set(['uri', 'cid', 'did', 'space', 'indexed_at', 'handle']);
|
|
16
|
+
export function columnName(fieldName) {
|
|
17
|
+
const snake = toSnakeCase(fieldName);
|
|
18
|
+
return ENVELOPE_COLUMNS.has(snake) ? `record_${snake}` : snake;
|
|
19
|
+
}
|
|
20
|
+
// Quote a column name to avoid conflicts with SQL reserved words
|
|
21
|
+
export function q(name) {
|
|
22
|
+
return `"${name}"`;
|
|
23
|
+
}
|
|
24
|
+
function mapType(prop, dialect) {
|
|
8
25
|
if (prop.type === 'string') {
|
|
9
26
|
if (prop.format === 'datetime')
|
|
10
|
-
return {
|
|
27
|
+
return { sqlType: dialect.typeMap.timestamp, isRef: false, isJson: false };
|
|
11
28
|
if (prop.format === 'at-uri')
|
|
12
|
-
return {
|
|
13
|
-
return {
|
|
29
|
+
return { sqlType: dialect.typeMap.text, isRef: true, isJson: false };
|
|
30
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
14
31
|
}
|
|
15
32
|
if (prop.type === 'integer')
|
|
16
|
-
return {
|
|
33
|
+
return { sqlType: dialect.typeMap.integer, isRef: false, isJson: false };
|
|
17
34
|
if (prop.type === 'boolean')
|
|
18
|
-
return {
|
|
35
|
+
return { sqlType: dialect.typeMap.boolean, isRef: false, isJson: false };
|
|
19
36
|
if (prop.type === 'bytes')
|
|
20
|
-
return {
|
|
37
|
+
return { sqlType: dialect.typeMap.blob, isRef: false, isJson: false };
|
|
21
38
|
if (prop.type === 'cid-link')
|
|
22
|
-
return {
|
|
39
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
23
40
|
if (prop.type === 'array')
|
|
24
|
-
return {
|
|
41
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
25
42
|
if (prop.type === 'blob')
|
|
26
|
-
return {
|
|
43
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
27
44
|
if (prop.type === 'union')
|
|
28
|
-
return {
|
|
45
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
29
46
|
if (prop.type === 'unknown')
|
|
30
|
-
return {
|
|
47
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
31
48
|
if (prop.type === 'object')
|
|
32
|
-
return {
|
|
49
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
33
50
|
if (prop.type === 'ref') {
|
|
34
51
|
// strongRef contains { uri, cid } — handled specially in generateTableSchema
|
|
35
52
|
if (prop.ref === 'com.atproto.repo.strongRef')
|
|
36
|
-
return {
|
|
37
|
-
return {
|
|
53
|
+
return { sqlType: 'STRONG_REF', isRef: true, isJson: false };
|
|
54
|
+
return { sqlType: dialect.jsonType, isRef: false, isJson: true };
|
|
38
55
|
}
|
|
39
|
-
return {
|
|
56
|
+
return { sqlType: dialect.typeMap.text, isRef: false, isJson: false };
|
|
40
57
|
}
|
|
41
58
|
// Recursively find all .json files in a directory
|
|
42
59
|
function findJsonFiles(dir) {
|
|
@@ -63,6 +80,20 @@ export function loadLexicons(lexiconsDir) {
|
|
|
63
80
|
}
|
|
64
81
|
return lexicons;
|
|
65
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Lexicons the validator cannot model but hatk legitimately keeps.
|
|
85
|
+
*
|
|
86
|
+
* A space type is how the indexer learns which collections a space holds, and
|
|
87
|
+
* a permission set is what an OAuth scope resolves through; both are real
|
|
88
|
+
* lexicons an app vendors on purpose. `validateLexicons` reports each as an
|
|
89
|
+
* unknown definition type, which would read as a fatal schema error at boot.
|
|
90
|
+
* Only these two are set aside — anything else unknown is still a mistake the
|
|
91
|
+
* validator should refuse.
|
|
92
|
+
*/
|
|
93
|
+
const UNMODELLED_DEF_TYPES = new Set(['space', 'permission-set']);
|
|
94
|
+
export function validatableLexicons(lexicons) {
|
|
95
|
+
return [...lexicons.values()].filter((lex) => !UNMODELLED_DEF_TYPES.has(lex.defs?.main?.type));
|
|
96
|
+
}
|
|
66
97
|
/**
|
|
67
98
|
* Discover collections by scanning lexicons for record-type definitions.
|
|
68
99
|
*/
|
|
@@ -121,7 +152,7 @@ function resolveRefDef(ref, defs, lexicons) {
|
|
|
121
152
|
return lexicons?.get(ref)?.defs?.main || null;
|
|
122
153
|
}
|
|
123
154
|
/** Resolve a single union ref to a branch schema */
|
|
124
|
-
function resolveUnionBranch(ref, collection, fieldName, defs, lexicons) {
|
|
155
|
+
function resolveUnionBranch(ref, collection, fieldName, defs, lexicons, dialect) {
|
|
125
156
|
let branchDef = null;
|
|
126
157
|
let branchName;
|
|
127
158
|
let fullType;
|
|
@@ -153,7 +184,10 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons) {
|
|
|
153
184
|
let arrayField;
|
|
154
185
|
let wrapperField;
|
|
155
186
|
let propSource = branchDef.properties;
|
|
156
|
-
|
|
187
|
+
// `required` has to travel with propSource: once the columns come from the
|
|
188
|
+
// item (or wrapper target) def, the outer branch's `required` describes the
|
|
189
|
+
// wrapper property, not these columns.
|
|
190
|
+
let branchRequired = new Set(branchDef.required || []);
|
|
157
191
|
// Check for single-property wrapper patterns
|
|
158
192
|
const propEntries = Object.entries(branchDef.properties);
|
|
159
193
|
if (propEntries.length === 1) {
|
|
@@ -166,6 +200,7 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons) {
|
|
|
166
200
|
isArray = true;
|
|
167
201
|
arrayField = onlyField;
|
|
168
202
|
propSource = itemDef.properties;
|
|
203
|
+
branchRequired = new Set(itemDef.required || []);
|
|
169
204
|
}
|
|
170
205
|
}
|
|
171
206
|
else if (onlyProp.type === 'ref' && onlyProp.ref) {
|
|
@@ -174,6 +209,7 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons) {
|
|
|
174
209
|
if (refDef?.type === 'object' && refDef.properties) {
|
|
175
210
|
wrapperField = onlyField;
|
|
176
211
|
propSource = refDef.properties;
|
|
212
|
+
branchRequired = new Set(refDef.required || []);
|
|
177
213
|
}
|
|
178
214
|
}
|
|
179
215
|
}
|
|
@@ -181,21 +217,22 @@ function resolveUnionBranch(ref, collection, fieldName, defs, lexicons) {
|
|
|
181
217
|
const tableName = `"${collection}__${snakeField}_${branchName}"`;
|
|
182
218
|
const columns = [];
|
|
183
219
|
for (const [propName, prop] of Object.entries(propSource)) {
|
|
184
|
-
const {
|
|
220
|
+
const { sqlType, isRef, isJson } = mapType(prop, dialect);
|
|
185
221
|
// Skip STRONG_REF expansion in branch tables — treat as JSON
|
|
186
|
-
const finalType =
|
|
222
|
+
const finalType = sqlType === 'STRONG_REF' ? dialect.jsonType : sqlType;
|
|
187
223
|
columns.push({
|
|
188
224
|
name: toSnakeCase(propName),
|
|
189
225
|
originalName: propName,
|
|
190
|
-
|
|
226
|
+
sqlType: finalType,
|
|
191
227
|
notNull: branchRequired.has(propName),
|
|
192
|
-
isRef: finalType !==
|
|
228
|
+
isRef: finalType !== dialect.jsonType && isRef,
|
|
229
|
+
isJson: isJson || sqlType === 'STRONG_REF',
|
|
193
230
|
});
|
|
194
231
|
}
|
|
195
232
|
return { type: fullType, branchName, tableName, columns, isArray, arrayField, wrapperField };
|
|
196
233
|
}
|
|
197
234
|
// Generate a TableSchema from a lexicon record definition
|
|
198
|
-
export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
235
|
+
export function generateTableSchema(nsid, lexicon, lexicons, dialect = DUCKDB_DIALECT) {
|
|
199
236
|
const mainDef = lexicon.defs?.main;
|
|
200
237
|
if (!mainDef || mainDef.type !== 'record') {
|
|
201
238
|
throw new Error(`Lexicon ${nsid} does not define a record type`);
|
|
@@ -214,7 +251,7 @@ export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
|
214
251
|
if (p.type === 'union' && p.refs) {
|
|
215
252
|
const branches = [];
|
|
216
253
|
for (const ref of p.refs) {
|
|
217
|
-
const branch = resolveUnionBranch(ref, nsid, fieldName, lexicon.defs, lexicons);
|
|
254
|
+
const branch = resolveUnionBranch(ref, nsid, fieldName, lexicon.defs, lexicons, dialect);
|
|
218
255
|
if (branch)
|
|
219
256
|
branches.push(branch);
|
|
220
257
|
}
|
|
@@ -223,11 +260,12 @@ export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
|
223
260
|
}
|
|
224
261
|
// Still add the JSON column for the raw union value
|
|
225
262
|
columns.push({
|
|
226
|
-
name:
|
|
263
|
+
name: columnName(fieldName),
|
|
227
264
|
originalName: fieldName,
|
|
228
|
-
|
|
265
|
+
sqlType: dialect.jsonType,
|
|
229
266
|
notNull: required.has(fieldName),
|
|
230
267
|
isRef: false,
|
|
268
|
+
isJson: true,
|
|
231
269
|
});
|
|
232
270
|
continue;
|
|
233
271
|
}
|
|
@@ -238,13 +276,14 @@ export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
|
238
276
|
const childColumns = [];
|
|
239
277
|
const itemRequired = new Set(p.items?.required || lexicon.defs?.[p.items?.ref?.slice(1)]?.required || []);
|
|
240
278
|
for (const [itemField, itemProp] of Object.entries(itemProps)) {
|
|
241
|
-
const {
|
|
279
|
+
const { sqlType, isRef, isJson } = mapType(itemProp, dialect);
|
|
242
280
|
childColumns.push({
|
|
243
281
|
name: toSnakeCase(itemField),
|
|
244
282
|
originalName: itemField,
|
|
245
|
-
|
|
283
|
+
sqlType,
|
|
246
284
|
notNull: itemRequired.has(itemField),
|
|
247
285
|
isRef,
|
|
286
|
+
isJson,
|
|
248
287
|
});
|
|
249
288
|
}
|
|
250
289
|
const snakeField = toSnakeCase(fieldName);
|
|
@@ -257,31 +296,34 @@ export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
|
257
296
|
continue;
|
|
258
297
|
}
|
|
259
298
|
}
|
|
260
|
-
const {
|
|
261
|
-
if (
|
|
299
|
+
const { sqlType, isRef, isJson } = mapType(p, dialect);
|
|
300
|
+
if (sqlType === 'STRONG_REF') {
|
|
262
301
|
// Expand strongRef into two columns: {name}_uri and {name}_cid
|
|
263
302
|
columns.push({
|
|
264
303
|
name: toSnakeCase(fieldName) + '_uri',
|
|
265
304
|
originalName: fieldName,
|
|
266
|
-
|
|
305
|
+
sqlType: dialect.typeMap.text,
|
|
267
306
|
notNull: required.has(fieldName),
|
|
268
307
|
isRef: true,
|
|
308
|
+
isJson: false,
|
|
269
309
|
});
|
|
270
310
|
columns.push({
|
|
271
311
|
name: toSnakeCase(fieldName) + '_cid',
|
|
272
312
|
originalName: fieldName + '__cid',
|
|
273
|
-
|
|
313
|
+
sqlType: dialect.typeMap.text,
|
|
274
314
|
notNull: required.has(fieldName),
|
|
275
315
|
isRef: false,
|
|
316
|
+
isJson: false,
|
|
276
317
|
});
|
|
277
318
|
}
|
|
278
319
|
else {
|
|
279
320
|
columns.push({
|
|
280
|
-
name:
|
|
321
|
+
name: columnName(fieldName),
|
|
281
322
|
originalName: fieldName,
|
|
282
|
-
|
|
323
|
+
sqlType,
|
|
283
324
|
notNull: required.has(fieldName),
|
|
284
325
|
isRef,
|
|
326
|
+
isJson,
|
|
285
327
|
});
|
|
286
328
|
}
|
|
287
329
|
}
|
|
@@ -296,26 +338,35 @@ export function generateTableSchema(nsid, lexicon, lexicons) {
|
|
|
296
338
|
};
|
|
297
339
|
}
|
|
298
340
|
// Generate CREATE TABLE SQL from a TableSchema
|
|
299
|
-
export function generateCreateTableSQL(schema) {
|
|
341
|
+
export function generateCreateTableSQL(schema, dialect = DUCKDB_DIALECT) {
|
|
342
|
+
// `space` is the permissioned space a row came from, and NULL for everything
|
|
343
|
+
// off the firehose. It is the only per-row visibility marker in the schema:
|
|
344
|
+
// a space record is readable by whoever the space's authority admits, so no
|
|
345
|
+
// read may serve one without knowing who is asking. NULL means "public repo
|
|
346
|
+
// data", which is what every existing row is.
|
|
300
347
|
const lines = [
|
|
301
348
|
' uri TEXT PRIMARY KEY',
|
|
302
349
|
' cid TEXT',
|
|
303
350
|
' did TEXT NOT NULL',
|
|
304
|
-
'
|
|
351
|
+
' space TEXT',
|
|
352
|
+
` indexed_at ${dialect.timestampType} NOT NULL`,
|
|
305
353
|
];
|
|
306
354
|
for (const col of schema.columns) {
|
|
307
355
|
const nullable = col.notNull ? ' NOT NULL' : '';
|
|
308
|
-
lines.push(` ${col.name} ${col.
|
|
356
|
+
lines.push(` ${q(col.name)} ${col.sqlType}${nullable}`);
|
|
309
357
|
}
|
|
310
358
|
const createTable = `CREATE TABLE IF NOT EXISTS ${schema.tableName} (\n${lines.join(',\n')}\n);`;
|
|
311
359
|
const prefix = schema.collection.replace(/\./g, '_');
|
|
312
360
|
const indexes = [
|
|
313
361
|
`CREATE INDEX IF NOT EXISTS idx_${prefix}_indexed ON ${schema.tableName}(indexed_at DESC);`,
|
|
314
362
|
`CREATE INDEX IF NOT EXISTS idx_${prefix}_author ON ${schema.tableName}(did);`,
|
|
363
|
+
// Every read that serves space rows filters on this column, and every read
|
|
364
|
+
// that does not still tests it for NULL.
|
|
365
|
+
`CREATE INDEX IF NOT EXISTS idx_${prefix}_space ON ${schema.tableName}(space);`,
|
|
315
366
|
];
|
|
316
367
|
// Index ref columns for hydration lookups
|
|
317
368
|
for (const refCol of schema.refColumns) {
|
|
318
|
-
indexes.push(`CREATE INDEX IF NOT EXISTS idx_${prefix}_${refCol} ON ${schema.tableName}(${refCol});`);
|
|
369
|
+
indexes.push(`CREATE INDEX IF NOT EXISTS idx_${prefix}_${refCol} ON ${schema.tableName}(${q(refCol)});`);
|
|
319
370
|
}
|
|
320
371
|
// Child table DDL
|
|
321
372
|
const childDDL = [];
|
|
@@ -323,16 +374,16 @@ export function generateCreateTableSQL(schema) {
|
|
|
323
374
|
const childLines = [' parent_uri TEXT NOT NULL', ' parent_did TEXT NOT NULL'];
|
|
324
375
|
for (const col of child.columns) {
|
|
325
376
|
const nullable = col.notNull ? ' NOT NULL' : '';
|
|
326
|
-
childLines.push(` ${col.name} ${col.
|
|
377
|
+
childLines.push(` ${q(col.name)} ${col.sqlType}${nullable}`);
|
|
327
378
|
}
|
|
328
379
|
childDDL.push(`CREATE TABLE IF NOT EXISTS ${child.tableName} (\n${childLines.join(',\n')}\n);`);
|
|
329
380
|
const childPrefix = `${prefix}__${toSnakeCase(child.fieldName)}`;
|
|
330
381
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_parent ON ${child.tableName}(parent_uri);`);
|
|
331
382
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_did ON ${child.tableName}(parent_did);`);
|
|
332
383
|
for (const col of child.columns) {
|
|
333
|
-
if (col.
|
|
384
|
+
if (col.isJson || col.sqlType === 'BLOB')
|
|
334
385
|
continue;
|
|
335
|
-
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_${col.name} ON ${child.tableName}(${col.name});`);
|
|
386
|
+
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${childPrefix}_${col.name} ON ${child.tableName}(${q(col.name)});`);
|
|
336
387
|
}
|
|
337
388
|
}
|
|
338
389
|
// Union branch table DDL
|
|
@@ -341,18 +392,49 @@ export function generateCreateTableSQL(schema) {
|
|
|
341
392
|
const branchLines = [' parent_uri TEXT NOT NULL', ' parent_did TEXT NOT NULL'];
|
|
342
393
|
for (const col of branch.columns) {
|
|
343
394
|
const nullable = col.notNull ? ' NOT NULL' : '';
|
|
344
|
-
branchLines.push(` ${col.name} ${col.
|
|
395
|
+
branchLines.push(` ${q(col.name)} ${col.sqlType}${nullable}`);
|
|
345
396
|
}
|
|
346
397
|
childDDL.push(`CREATE TABLE IF NOT EXISTS ${branch.tableName} (\n${branchLines.join(',\n')}\n);`);
|
|
347
398
|
const branchPrefix = branch.tableName.replace(/"/g, '').replace(/\./g, '_');
|
|
348
399
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_parent ON ${branch.tableName}(parent_uri);`);
|
|
349
400
|
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_did ON ${branch.tableName}(parent_did);`);
|
|
350
401
|
for (const col of branch.columns) {
|
|
351
|
-
if (col.
|
|
402
|
+
if (col.isJson || col.sqlType === 'BLOB')
|
|
352
403
|
continue;
|
|
353
|
-
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_${col.name} ON ${branch.tableName}(${col.name});`);
|
|
404
|
+
childDDL.push(`CREATE INDEX IF NOT EXISTS idx_${branchPrefix}_${col.name} ON ${branch.tableName}(${q(col.name)});`);
|
|
354
405
|
}
|
|
355
406
|
}
|
|
356
407
|
}
|
|
357
408
|
return [createTable, ...indexes, ...childDDL].join('\n');
|
|
358
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Build table schemas and DDL from lexicons and collections.
|
|
412
|
+
* Shared by main.ts (server boot) and cli.ts (hatk schema command).
|
|
413
|
+
*/
|
|
414
|
+
export function buildSchemas(lexicons, collections, dialect = DUCKDB_DIALECT) {
|
|
415
|
+
const schemas = [];
|
|
416
|
+
const ddlStatements = [];
|
|
417
|
+
for (const nsid of collections) {
|
|
418
|
+
const lexicon = lexicons.get(nsid);
|
|
419
|
+
if (!lexicon) {
|
|
420
|
+
const genericDDL = `CREATE TABLE IF NOT EXISTS "${nsid}" (
|
|
421
|
+
uri TEXT PRIMARY KEY,
|
|
422
|
+
cid TEXT,
|
|
423
|
+
did TEXT NOT NULL,
|
|
424
|
+
space TEXT,
|
|
425
|
+
indexed_at ${dialect.timestampType} NOT NULL,
|
|
426
|
+
data ${dialect.jsonType}
|
|
427
|
+
);
|
|
428
|
+
CREATE INDEX IF NOT EXISTS idx_${nsid.replace(/\./g, '_')}_indexed ON "${nsid}"(indexed_at DESC);
|
|
429
|
+
CREATE INDEX IF NOT EXISTS idx_${nsid.replace(/\./g, '_')}_author ON "${nsid}"(did);
|
|
430
|
+
CREATE INDEX IF NOT EXISTS idx_${nsid.replace(/\./g, '_')}_space ON "${nsid}"(space);`;
|
|
431
|
+
schemas.push({ collection: nsid, tableName: `"${nsid}"`, columns: [], refColumns: [], children: [], unions: [] });
|
|
432
|
+
ddlStatements.push(genericDDL);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const schema = generateTableSchema(nsid, lexicon, lexicons, dialect);
|
|
436
|
+
schemas.push(schema);
|
|
437
|
+
ddlStatements.push(generateCreateTableSQL(schema, dialect));
|
|
438
|
+
}
|
|
439
|
+
return { schemas, ddlStatements };
|
|
440
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const handler: (request: Request) => Promise<Response>;
|
|
2
|
+
/** Re-scan server/ directory to pick up changed handlers in dev mode. */
|
|
3
|
+
export declare function reloadServer(): Promise<void>;
|
|
4
|
+
export { renderPage } from './renderer.ts';
|
|
5
|
+
export { getRenderer } from './renderer.ts';
|
|
6
|
+
export { callXrpc } from './xrpc.ts';
|
|
7
|
+
export { parseSessionCookie, getSessionCookieName } from './oauth/session.ts';
|
|
8
|
+
//# sourceMappingURL=dev-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../src/dev-entry.ts"],"names":[],"mappings":"AAiIA,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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev mode entry point — loaded through Vite's module runner.
|
|
3
|
+
* Boots hatk infrastructure and exports the fetch handler.
|
|
4
|
+
*/
|
|
5
|
+
import { loadConfig } from "./config.js";
|
|
6
|
+
import { validatableLexicons, loadLexicons, storeLexicons, discoverCollections, buildSchemas, } from "./database/schema.js";
|
|
7
|
+
import { discoverViews } from "./views.js";
|
|
8
|
+
import { initDatabase, migrateSchema, getSchemaDump } from "./database/db.js";
|
|
9
|
+
import { createAdapter } from "./database/adapter-factory.js";
|
|
10
|
+
import { getDialect } from "./database/dialect.js";
|
|
11
|
+
import { setSearchPort } from "./database/fts.js";
|
|
12
|
+
import { configureRelay, configureCdn, configureOAuth } from "./xrpc.js";
|
|
13
|
+
import { initOAuth } from "./oauth/server.js";
|
|
14
|
+
import { initServer } from "./server-init.js";
|
|
15
|
+
import { createHandler, registerCoreHandlers } from "./server.js";
|
|
16
|
+
import { setPrivateCollections } from "./private-collections.js";
|
|
17
|
+
import { auxCursorKey, startAuxIndexer, startIndexer, sweepReferences } from "./indexer.js";
|
|
18
|
+
import { getCursor } from "./database/db.js";
|
|
19
|
+
import { runBackfill, configurePlc } from "./backfill.js";
|
|
20
|
+
import { rebuildAllIndexes } from "./database/fts.js";
|
|
21
|
+
import { relayHttpUrl } from "./config.js";
|
|
22
|
+
import { validateLexicons } from '@bigmoves/lexicon';
|
|
23
|
+
import { log } from "./logger.js";
|
|
24
|
+
import { mkdirSync } from 'node:fs';
|
|
25
|
+
import { dirname, resolve } from 'node:path';
|
|
26
|
+
process.env.DEV_MODE = '1';
|
|
27
|
+
// Boot sequence (mirrors main.ts but exports handler instead of starting server)
|
|
28
|
+
const configPath = 'hatk.config.ts';
|
|
29
|
+
const configDir = dirname(resolve(configPath));
|
|
30
|
+
const config = await loadConfig(configPath);
|
|
31
|
+
configureRelay(config.relay);
|
|
32
|
+
configurePlc(config.plc);
|
|
33
|
+
configureCdn(config.cdn);
|
|
34
|
+
const lexicons = loadLexicons(resolve(configDir, 'lexicons'));
|
|
35
|
+
const lexiconErrors = validateLexicons(validatableLexicons(lexicons));
|
|
36
|
+
if (lexiconErrors) {
|
|
37
|
+
for (const [nsid, errors] of Object.entries(lexiconErrors)) {
|
|
38
|
+
for (const err of errors)
|
|
39
|
+
console.error(`Invalid lexicon ${nsid}: ${err}`);
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Invalid lexicons');
|
|
42
|
+
}
|
|
43
|
+
storeLexicons(lexicons);
|
|
44
|
+
const collections = config.collections.length > 0 ? config.collections : discoverCollections(lexicons);
|
|
45
|
+
discoverViews();
|
|
46
|
+
const engineDialect = getDialect(config.databaseEngine);
|
|
47
|
+
const { schemas, ddlStatements } = buildSchemas(lexicons, collections, engineDialect);
|
|
48
|
+
if (config.database !== ':memory:') {
|
|
49
|
+
mkdirSync(dirname(config.database), { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
const { adapter, searchPort } = await createAdapter(config.databaseEngine);
|
|
52
|
+
setSearchPort(searchPort);
|
|
53
|
+
await initDatabase(adapter, config.database, schemas, ddlStatements);
|
|
54
|
+
await migrateSchema(schemas);
|
|
55
|
+
// Write db/schema.sql
|
|
56
|
+
try {
|
|
57
|
+
const { mkdirSync, writeFileSync } = await import('node:fs');
|
|
58
|
+
const schemaDir = resolve(configDir, 'db');
|
|
59
|
+
mkdirSync(schemaDir, { recursive: true });
|
|
60
|
+
const schemaDump = await getSchemaDump();
|
|
61
|
+
writeFileSync(resolve(schemaDir, 'schema.sql'), `-- This file is auto-generated by hatk on startup. Do not edit.\n-- Database engine: ${config.databaseEngine}\n\n${schemaDump}\n`);
|
|
62
|
+
log(`[hatk] Schema written to db/schema.sql`);
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
// Initialize handlers from server/ directory
|
|
66
|
+
await initServer(resolve(configDir, 'server'));
|
|
67
|
+
// Register built-in dev.hatk.* handlers so callXrpc() can find them
|
|
68
|
+
setPrivateCollections(config.privateCollections);
|
|
69
|
+
registerCoreHandlers(collections, config.oauth);
|
|
70
|
+
configureOAuth(config.oauth);
|
|
71
|
+
if (config.oauth) {
|
|
72
|
+
await initOAuth(config.oauth, config.plc, config.relay);
|
|
73
|
+
}
|
|
74
|
+
// Start indexer
|
|
75
|
+
const collectionSet = new Set(collections);
|
|
76
|
+
// Same escape hatch as main.ts: boot from live instead of the saved cursor.
|
|
77
|
+
const ignoreSavedCursor = Boolean(process.env.HATK_IGNORE_SAVED_CURSOR);
|
|
78
|
+
if (ignoreSavedCursor)
|
|
79
|
+
log('[dev] HATK_IGNORE_SAVED_CURSOR set — starting indexer from live');
|
|
80
|
+
const cursor = ignoreSavedCursor ? null : await getCursor('relay');
|
|
81
|
+
startIndexer({
|
|
82
|
+
relayUrl: config.relay,
|
|
83
|
+
plcUrl: config.plc,
|
|
84
|
+
collections: collectionSet,
|
|
85
|
+
signalCollections: config.backfill.signalCollections ? new Set(config.backfill.signalCollections) : undefined,
|
|
86
|
+
references: config.backfill.references,
|
|
87
|
+
pinnedRepos: config.backfill.repos ? new Set(config.backfill.repos) : undefined,
|
|
88
|
+
cursor,
|
|
89
|
+
fetchTimeout: config.backfill.fetchTimeout,
|
|
90
|
+
maxRetries: config.backfill.maxRetries,
|
|
91
|
+
parallelism: config.backfill.parallelism,
|
|
92
|
+
ftsRebuildInterval: config.ftsRebuildInterval,
|
|
93
|
+
});
|
|
94
|
+
void sweepReferences();
|
|
95
|
+
for (const relayUrl of config.relays) {
|
|
96
|
+
const auxCursor = ignoreSavedCursor ? null : await getCursor(auxCursorKey(relayUrl));
|
|
97
|
+
startAuxIndexer({ relayUrl, collections: collectionSet, cursor: auxCursor });
|
|
98
|
+
}
|
|
99
|
+
// Run backfill in background (no restart in dev mode)
|
|
100
|
+
runBackfill({
|
|
101
|
+
pdsUrl: relayHttpUrl(config.relay),
|
|
102
|
+
extraPdsUrls: config.relays.map(relayHttpUrl),
|
|
103
|
+
plcUrl: config.plc,
|
|
104
|
+
collections: collectionSet,
|
|
105
|
+
config: config.backfill,
|
|
106
|
+
})
|
|
107
|
+
.then(() => rebuildAllIndexes(Array.from(collectionSet)))
|
|
108
|
+
.catch((err) => console.error('[backfill]', err.message));
|
|
109
|
+
// Export the handler for Vite middleware
|
|
110
|
+
export const handler = createHandler({
|
|
111
|
+
collections: Array.from(collectionSet),
|
|
112
|
+
publicDir: null, // Vite serves static assets in dev
|
|
113
|
+
oauth: config.oauth,
|
|
114
|
+
admins: config.admins,
|
|
115
|
+
});
|
|
116
|
+
/** Re-scan server/ directory to pick up changed handlers in dev mode. */
|
|
117
|
+
export async function reloadServer() {
|
|
118
|
+
await initServer(resolve(configDir, 'server'));
|
|
119
|
+
}
|
|
120
|
+
export { renderPage } from "./renderer.js";
|
|
121
|
+
export { getRenderer } from "./renderer.js";
|
|
122
|
+
export { callXrpc } from "./xrpc.js";
|
|
123
|
+
export { parseSessionCookie, getSessionCookieName } from "./oauth/session.js";
|
|
124
|
+
log(`[hatk] Dev server ready`);
|
|
125
|
+
log(` Relay: ${config.relay}${config.relays.length ? ` (+ ${config.relays.join(', ')})` : ''}`);
|
|
126
|
+
log(` Database: ${config.database}`);
|
|
127
|
+
log(` Collections: ${collections.join(', ')}`);
|
package/dist/feeds.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BaseContext, Row } from './hydrate.ts';
|
|
2
2
|
import type { Checked } from './lex-types.ts';
|
|
3
|
-
export type {
|
|
3
|
+
export type { BaseContext, Row };
|
|
4
4
|
export interface FeedResult {
|
|
5
5
|
uris: string[];
|
|
6
6
|
cursor?: string;
|
|
@@ -16,7 +16,9 @@ export interface PaginateResult<T> {
|
|
|
16
16
|
}
|
|
17
17
|
export interface FeedContext {
|
|
18
18
|
db: {
|
|
19
|
-
query: (sql: string, params?:
|
|
19
|
+
query: (sql: string, params?: unknown[]) => Promise<unknown[]>;
|
|
20
|
+
/** Raw SQL with the permissioned-space guard off. See `spaces/guard.ts`. */
|
|
21
|
+
unfiltered: (sql: string, params?: unknown[]) => Promise<unknown[]>;
|
|
20
22
|
};
|
|
21
23
|
params: Record<string, string>;
|
|
22
24
|
cursor?: string;
|
|
@@ -34,6 +36,18 @@ export interface FeedContext {
|
|
|
34
36
|
paginate: <T extends {
|
|
35
37
|
uri: string;
|
|
36
38
|
}>(sql: string, opts?: PaginateOpts) => Promise<PaginateResult<T>>;
|
|
39
|
+
/**
|
|
40
|
+
* The permissioned-space gate, for SQL this feed builds without `paginate`.
|
|
41
|
+
*
|
|
42
|
+
* `paginate` applies it already. This is for a feed that runs its own query
|
|
43
|
+
* — a raw `ctx.db.query` over a record table has to carry it, or it serves
|
|
44
|
+
* space rows to whoever asks. See `spaceFilterSql`.
|
|
45
|
+
*/
|
|
46
|
+
spaceFilter: (alias: string, startIdx: number) => {
|
|
47
|
+
sql: string;
|
|
48
|
+
params: string[];
|
|
49
|
+
nextIdx: number;
|
|
50
|
+
};
|
|
37
51
|
}
|
|
38
52
|
type FeedGenerate = (ctx: FeedContext & {
|
|
39
53
|
ok: (value: FeedResult) => Checked<FeedResult>;
|
|
@@ -43,17 +57,17 @@ type FeedOpts = {
|
|
|
43
57
|
view?: string;
|
|
44
58
|
label: string;
|
|
45
59
|
generate: FeedGenerate;
|
|
46
|
-
hydrate?: (ctx:
|
|
60
|
+
hydrate?: (ctx: BaseContext, items: Row<any>[]) => Promise<unknown[]>;
|
|
47
61
|
} | {
|
|
48
62
|
collection?: never;
|
|
49
63
|
view?: never;
|
|
50
64
|
label: string;
|
|
51
65
|
generate: FeedGenerate;
|
|
52
|
-
hydrate: (ctx:
|
|
66
|
+
hydrate: (ctx: BaseContext, items: Row<any>[]) => Promise<unknown[]>;
|
|
53
67
|
};
|
|
54
68
|
export declare function createPaginate(deps: {
|
|
55
69
|
db: {
|
|
56
|
-
query: (sql: string, params?:
|
|
70
|
+
query: (sql: string, params?: unknown[]) => Promise<unknown[]>;
|
|
57
71
|
};
|
|
58
72
|
cursor?: string;
|
|
59
73
|
limit: number;
|
|
@@ -70,14 +84,18 @@ export declare function defineFeed(opts: FeedOpts): {
|
|
|
70
84
|
collection: string;
|
|
71
85
|
view?: string;
|
|
72
86
|
label: string;
|
|
73
|
-
hydrate?: (ctx:
|
|
87
|
+
hydrate?: (ctx: BaseContext, items: Row<any>[]) => Promise<unknown[]>;
|
|
88
|
+
__type: "feed";
|
|
74
89
|
} | {
|
|
75
90
|
generate: (ctx: any) => Promise<Checked<FeedResult>>;
|
|
76
91
|
collection?: never;
|
|
77
92
|
view?: never;
|
|
78
93
|
label: string;
|
|
79
|
-
hydrate: (ctx:
|
|
94
|
+
hydrate: (ctx: BaseContext, items: Row<any>[]) => Promise<unknown[]>;
|
|
95
|
+
__type: "feed";
|
|
80
96
|
};
|
|
97
|
+
/** Register a single feed from a scanned server/ module. */
|
|
98
|
+
export declare function registerFeed(name: string, generator: ReturnType<typeof defineFeed>): void;
|
|
81
99
|
export declare function initFeeds(feedsDir: string): Promise<void>;
|
|
82
100
|
/** Execute a feed and run its hydrate pipeline if present. */
|
|
83
101
|
export declare function executeFeed(name: string, params: Record<string, string>, cursor: string | undefined, limit: number, viewer?: {
|
package/dist/feeds.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feeds.d.ts","sourceRoot":"","sources":["../src/feeds.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feeds.d.ts","sourceRoot":"","sources":["../src/feeds.ts"],"names":[],"mappings":"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"}
|