@hatk/hatk 0.0.1-alpha.3 → 0.0.1-alpha.30
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 +94 -0
- package/dist/backfill.d.ts +60 -1
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +166 -32
- package/dist/car.d.ts +59 -1
- package/dist/car.d.ts.map +1 -1
- package/dist/car.js +179 -7
- package/dist/cbor.d.ts +37 -0
- package/dist/cbor.d.ts.map +1 -1
- package/dist/cbor.js +36 -3
- package/dist/cid.d.ts +37 -0
- package/dist/cid.d.ts.map +1 -1
- package/dist/cid.js +38 -3
- package/dist/cli.js +356 -123
- package/dist/config.d.ts +12 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +36 -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 +18 -0
- package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
- package/dist/database/adapters/sqlite-search.js +38 -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 +87 -0
- package/dist/database/db.d.ts +149 -0
- package/dist/database/db.d.ts.map +1 -0
- package/dist/database/db.js +1460 -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/database/fts.d.ts +24 -0
- package/dist/database/fts.d.ts.map +1 -0
- package/dist/database/fts.js +777 -0
- 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 +44 -0
- package/dist/database/ports.d.ts.map +1 -0
- package/dist/database/ports.js +1 -0
- package/dist/database/schema.d.ts +60 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +388 -0
- package/dist/db.d.ts +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +4 -38
- package/dist/dev-entry.d.ts +8 -0
- package/dist/dev-entry.d.ts.map +1 -0
- package/dist/dev-entry.js +109 -0
- package/dist/feeds.d.ts +4 -0
- package/dist/feeds.d.ts.map +1 -1
- package/dist/feeds.js +42 -3
- package/dist/fts.d.ts.map +1 -1
- package/dist/fts.js +5 -0
- package/dist/hooks.d.ts +22 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +75 -0
- package/dist/hydrate.js +1 -1
- package/dist/indexer.d.ts +20 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +48 -6
- package/dist/labels.d.ts +34 -0
- package/dist/labels.d.ts.map +1 -1
- package/dist/labels.js +63 -3
- 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 +131 -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.map +1 -1
- package/dist/oauth/db.js +41 -15
- package/dist/oauth/server.d.ts +2 -0
- package/dist/oauth/server.d.ts.map +1 -1
- package/dist/oauth/server.js +102 -7
- package/dist/oauth/session.d.ts +9 -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 +103 -5
- package/dist/pds-proxy.d.ts +39 -0
- package/dist/pds-proxy.d.ts.map +1 -0
- package/dist/pds-proxy.js +173 -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/schema.d.ts +8 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +29 -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 +59 -0
- package/dist/server.d.ts +26 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +487 -616
- package/dist/setup.d.ts +28 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +50 -3
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +38 -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 +252 -66
- package/dist/xrpc.d.ts +36 -0
- package/dist/xrpc.d.ts.map +1 -1
- package/dist/xrpc.js +124 -3
- package/package.json +12 -5
package/dist/xrpc.d.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import type { Row, FlatRow } from './lex-types.ts';
|
|
2
2
|
export type { Row, FlatRow };
|
|
3
|
+
/** Thrown from XRPC handlers to return a 400 response with an error message. */
|
|
3
4
|
export declare class InvalidRequestError extends Error {
|
|
4
5
|
status: number;
|
|
5
6
|
errorName?: string;
|
|
6
7
|
constructor(message: string, errorName?: string);
|
|
7
8
|
}
|
|
9
|
+
/** Thrown from XRPC handlers to return a 404 response. */
|
|
8
10
|
export declare class NotFoundError extends InvalidRequestError {
|
|
9
11
|
status: number;
|
|
10
12
|
constructor(message?: string);
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Context passed to every XRPC handler. Provides database access, pagination
|
|
16
|
+
* helpers, viewer auth, record resolution, full-text search, label queries,
|
|
17
|
+
* and blob URL generation.
|
|
18
|
+
*
|
|
19
|
+
* @typeParam P - Query parameter types (derived from lexicon)
|
|
20
|
+
* @typeParam Records - Map of collection NSID → record type (from generated types)
|
|
21
|
+
* @typeParam I - Input body type for procedure calls
|
|
22
|
+
*/
|
|
12
23
|
export interface XrpcContext<P = Record<string, string>, Records extends Record<string, any> = Record<string, any>, I = unknown> {
|
|
13
24
|
db: {
|
|
14
25
|
query: (sql: string, params?: any[]) => Promise<any[]>;
|
|
@@ -43,11 +54,36 @@ export interface XrpcContext<P = Record<string, string>, Records extends Record<
|
|
|
43
54
|
labels: (uris: string[]) => Promise<Map<string, any[]>>;
|
|
44
55
|
blobUrl: (did: string, ref: unknown, preset?: 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize') => string | undefined;
|
|
45
56
|
}
|
|
57
|
+
/** Set the relay URL used for blob URL generation. Called once during boot. */
|
|
46
58
|
export declare function configureRelay(relay: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Generate a CDN URL for a blob ref. Uses the PDS directly in local dev,
|
|
61
|
+
* or the Bluesky CDN (`cdn.bsky.app`) in production.
|
|
62
|
+
*/
|
|
47
63
|
export declare function blobUrl(did: string, ref: unknown, preset?: 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize'): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Discover and load XRPC handler modules from the `xrpc/` directory.
|
|
66
|
+
* Directory nesting maps to NSID segments. Parameters are validated and
|
|
67
|
+
* coerced against the matching lexicon definition.
|
|
68
|
+
*/
|
|
48
69
|
export declare function initXrpc(xrpcDir: string): Promise<void>;
|
|
70
|
+
/** Register a single XRPC handler from a scanned server/ module. */
|
|
71
|
+
export declare function registerXrpcHandler(nsid: string, handlerModule: {
|
|
72
|
+
handler: (ctx: any) => Promise<any>;
|
|
73
|
+
}): void;
|
|
74
|
+
/** Execute a registered XRPC handler by name. Returns null if no handler matches. */
|
|
49
75
|
export declare function executeXrpc(name: string, params: Record<string, string>, cursor: string | undefined, limit: number, viewer?: {
|
|
50
76
|
did: string;
|
|
51
77
|
} | null, input?: unknown): Promise<any | null>;
|
|
78
|
+
/** Call a registered XRPC handler directly (no HTTP). For use in SSR renderers. */
|
|
79
|
+
export declare function callXrpc(nsid: string, params?: Record<string, any>, input?: unknown): Promise<any>;
|
|
80
|
+
/**
|
|
81
|
+
* Register a core XRPC handler directly (no XrpcContext wrapping).
|
|
82
|
+
* Used for built-in dev.hatk.* handlers that manage their own dependencies.
|
|
83
|
+
*/
|
|
84
|
+
export declare function registerCoreXrpcHandler(nsid: string, fn: (params: Record<string, string>, cursor: string | undefined, limit: number, viewer: {
|
|
85
|
+
did: string;
|
|
86
|
+
} | null, input?: unknown) => Promise<any>): void;
|
|
87
|
+
/** Return all registered XRPC method names. */
|
|
52
88
|
export declare function listXrpc(): string[];
|
|
53
89
|
//# sourceMappingURL=xrpc.d.ts.map
|
package/dist/xrpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"xrpc.d.ts","sourceRoot":"","sources":["../src/xrpc.ts"],"names":[],"mappings":"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"}
|
package/dist/xrpc.js
CHANGED
|
@@ -6,12 +6,33 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* XRPC method handler system for serving AT Protocol endpoints.
|
|
11
|
+
*
|
|
12
|
+
* Place handler modules in the `xrpc/` directory, nested by NSID segments
|
|
13
|
+
* (e.g. `xrpc/app/bsky/feed/getAuthorFeed.ts` → `app.bsky.feed.getAuthorFeed`).
|
|
14
|
+
* Each module default-exports a `{ handler }` function that receives an
|
|
15
|
+
* {@link XrpcContext} with database access, query params, pagination, and
|
|
16
|
+
* viewer auth.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // xrpc/xyz/statusphere/getStatuses.ts
|
|
21
|
+
* import { defineXrpc } from '../../hatk.generated.ts'
|
|
22
|
+
*
|
|
23
|
+
* export default defineXrpc('xyz.statusphere.getStatuses', async (ctx) => {
|
|
24
|
+
* const rows = await ctx.db.query('SELECT * FROM statusphere_status LIMIT ?', [ctx.limit])
|
|
25
|
+
* return { statuses: rows }
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
9
29
|
import { resolve, relative } from 'node:path';
|
|
10
30
|
import { readdirSync, statSync } from 'node:fs';
|
|
11
31
|
import { log } from "./logger.js";
|
|
12
|
-
import { querySQL, runSQL, packCursor, unpackCursor, isTakendownDid, filterTakendownDids, searchRecords, findUriByFields, lookupByFieldBatch, countByFieldBatch, queryLabelsForUris, } from "./db.js";
|
|
32
|
+
import { querySQL, runSQL, packCursor, unpackCursor, isTakendownDid, filterTakendownDids, searchRecords, findUriByFields, lookupByFieldBatch, countByFieldBatch, queryLabelsForUris, } from "./database/db.js";
|
|
13
33
|
import { resolveRecords } from "./hydrate.js";
|
|
14
|
-
import { getLexicon } from "./schema.js";
|
|
34
|
+
import { getLexicon } from "./database/schema.js";
|
|
35
|
+
/** Thrown from XRPC handlers to return a 400 response with an error message. */
|
|
15
36
|
export class InvalidRequestError extends Error {
|
|
16
37
|
status = 400;
|
|
17
38
|
errorName;
|
|
@@ -20,6 +41,7 @@ export class InvalidRequestError extends Error {
|
|
|
20
41
|
this.errorName = errorName;
|
|
21
42
|
}
|
|
22
43
|
}
|
|
44
|
+
/** Thrown from XRPC handlers to return a 404 response. */
|
|
23
45
|
export class NotFoundError extends InvalidRequestError {
|
|
24
46
|
status = 404;
|
|
25
47
|
constructor(message = 'Not found') {
|
|
@@ -27,9 +49,14 @@ export class NotFoundError extends InvalidRequestError {
|
|
|
27
49
|
}
|
|
28
50
|
}
|
|
29
51
|
let _relayUrl = '';
|
|
52
|
+
/** Set the relay URL used for blob URL generation. Called once during boot. */
|
|
30
53
|
export function configureRelay(relay) {
|
|
31
54
|
_relayUrl = relay;
|
|
32
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate a CDN URL for a blob ref. Uses the PDS directly in local dev,
|
|
58
|
+
* or the Bluesky CDN (`cdn.bsky.app`) in production.
|
|
59
|
+
*/
|
|
33
60
|
export function blobUrl(did, ref, preset = 'avatar') {
|
|
34
61
|
if (!ref)
|
|
35
62
|
return undefined;
|
|
@@ -42,6 +69,7 @@ export function blobUrl(did, ref, preset = 'avatar') {
|
|
|
42
69
|
return `https://cdn.bsky.app/img/${preset}/plain/${did}/${p.ref.$link}@jpeg`;
|
|
43
70
|
}
|
|
44
71
|
const handlers = new Map();
|
|
72
|
+
/** Recursively collect .ts/.js files in a directory, skipping files prefixed with `_`. */
|
|
45
73
|
function walkDir(dir) {
|
|
46
74
|
const results = [];
|
|
47
75
|
try {
|
|
@@ -58,6 +86,11 @@ function walkDir(dir) {
|
|
|
58
86
|
catch { }
|
|
59
87
|
return results.sort();
|
|
60
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Discover and load XRPC handler modules from the `xrpc/` directory.
|
|
91
|
+
* Directory nesting maps to NSID segments. Parameters are validated and
|
|
92
|
+
* coerced against the matching lexicon definition.
|
|
93
|
+
*/
|
|
61
94
|
export async function initXrpc(xrpcDir) {
|
|
62
95
|
const files = walkDir(xrpcDir);
|
|
63
96
|
if (files.length === 0)
|
|
@@ -65,7 +98,7 @@ export async function initXrpc(xrpcDir) {
|
|
|
65
98
|
for (const scriptPath of files) {
|
|
66
99
|
const rel = relative(xrpcDir, scriptPath).replace(/\.(ts|js)$/, '');
|
|
67
100
|
const name = rel.replace(/[\\/]/g, '.');
|
|
68
|
-
const mod = await import(__rewriteRelativeImportExtension(scriptPath));
|
|
101
|
+
const mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ `${scriptPath}?t=${Date.now()}`));
|
|
69
102
|
const handler = mod.default;
|
|
70
103
|
// Extract param schema from lexicon for validation and defaults
|
|
71
104
|
const lexicon = getLexicon(name);
|
|
@@ -128,12 +161,100 @@ export async function initXrpc(xrpcDir) {
|
|
|
128
161
|
log(`[xrpc] discovered: ${name}`);
|
|
129
162
|
}
|
|
130
163
|
}
|
|
164
|
+
/** Register a single XRPC handler from a scanned server/ module. */
|
|
165
|
+
export function registerXrpcHandler(nsid, handlerModule) {
|
|
166
|
+
const lexicon = getLexicon(nsid);
|
|
167
|
+
const paramsDef = lexicon?.defs?.main?.parameters;
|
|
168
|
+
const requiredParams = paramsDef?.required || [];
|
|
169
|
+
const paramProperties = paramsDef?.properties || {};
|
|
170
|
+
handlers.set(nsid, {
|
|
171
|
+
name: nsid,
|
|
172
|
+
execute: async (params, cursor, limit, viewer, input) => {
|
|
173
|
+
for (const [key, def] of Object.entries(paramProperties)) {
|
|
174
|
+
if (params[key] == null && def.default != null) {
|
|
175
|
+
params[key] = String(def.default);
|
|
176
|
+
}
|
|
177
|
+
if (params[key] != null && def.type === 'integer') {
|
|
178
|
+
params[key] = Number(params[key]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const param of requiredParams) {
|
|
182
|
+
if (!params[param]) {
|
|
183
|
+
throw new InvalidRequestError(`Missing required parameter: ${param}`, 'InvalidRequest');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const ctx = {
|
|
187
|
+
db: { query: querySQL, run: runSQL },
|
|
188
|
+
params,
|
|
189
|
+
input: input || {},
|
|
190
|
+
cursor,
|
|
191
|
+
limit,
|
|
192
|
+
viewer,
|
|
193
|
+
packCursor,
|
|
194
|
+
unpackCursor,
|
|
195
|
+
isTakendown: isTakendownDid,
|
|
196
|
+
filterTakendownDids,
|
|
197
|
+
search: searchRecords,
|
|
198
|
+
resolve: resolveRecords,
|
|
199
|
+
lookup: async (collection, field, values) => {
|
|
200
|
+
if (values.length === 0)
|
|
201
|
+
return new Map();
|
|
202
|
+
const unique = [...new Set(values.filter(Boolean))];
|
|
203
|
+
return lookupByFieldBatch(collection, field, unique);
|
|
204
|
+
},
|
|
205
|
+
count: async (collection, field, values) => {
|
|
206
|
+
if (values.length === 0)
|
|
207
|
+
return new Map();
|
|
208
|
+
const unique = [...new Set(values.filter(Boolean))];
|
|
209
|
+
return countByFieldBatch(collection, field, unique);
|
|
210
|
+
},
|
|
211
|
+
exists: async (collection, filters) => {
|
|
212
|
+
const conditions = Object.entries(filters).map(([field, value]) => ({ field, value }));
|
|
213
|
+
const uri = await findUriByFields(collection, conditions);
|
|
214
|
+
return uri !== null;
|
|
215
|
+
},
|
|
216
|
+
labels: queryLabelsForUris,
|
|
217
|
+
blobUrl,
|
|
218
|
+
};
|
|
219
|
+
return handlerModule.handler(ctx);
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** Execute a registered XRPC handler by name. Returns null if no handler matches. */
|
|
131
224
|
export async function executeXrpc(name, params, cursor, limit, viewer, input) {
|
|
132
225
|
const handler = handlers.get(name);
|
|
133
226
|
if (!handler)
|
|
134
227
|
return null;
|
|
135
228
|
return handler.execute(params, cursor, limit, viewer || null, input);
|
|
136
229
|
}
|
|
230
|
+
/** Call a registered XRPC handler directly (no HTTP). For use in SSR renderers. */
|
|
231
|
+
export async function callXrpc(nsid, params = {}, input) {
|
|
232
|
+
const viewer = globalThis.__hatk_viewer ?? null;
|
|
233
|
+
// In externalized module context (e.g. SSR), delegate to the runner's callXrpc via globalThis.
|
|
234
|
+
// The runner's module instance has all registered handlers; this (Node's) instance may not.
|
|
235
|
+
if (handlers.size === 0 && globalThis.__hatk_callXrpc) {
|
|
236
|
+
return globalThis.__hatk_callXrpc(nsid, params, input);
|
|
237
|
+
}
|
|
238
|
+
const stringParams = {};
|
|
239
|
+
for (const [k, v] of Object.entries(params)) {
|
|
240
|
+
if (v != null)
|
|
241
|
+
stringParams[k] = String(v);
|
|
242
|
+
}
|
|
243
|
+
const limit = params.limit ? Number(params.limit) : 20;
|
|
244
|
+
const cursor = params.cursor ?? undefined;
|
|
245
|
+
const result = await executeXrpc(nsid, stringParams, cursor, limit, viewer, input);
|
|
246
|
+
if (result === null)
|
|
247
|
+
throw new Error(`No XRPC handler registered for ${nsid}`);
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Register a core XRPC handler directly (no XrpcContext wrapping).
|
|
252
|
+
* Used for built-in dev.hatk.* handlers that manage their own dependencies.
|
|
253
|
+
*/
|
|
254
|
+
export function registerCoreXrpcHandler(nsid, fn) {
|
|
255
|
+
handlers.set(nsid, { name: nsid, execute: fn });
|
|
256
|
+
}
|
|
257
|
+
/** Return all registered XRPC method names. */
|
|
137
258
|
export function listXrpc() {
|
|
138
259
|
return Array.from(handlers.keys());
|
|
139
260
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatk/hatk",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.30",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"bin": {
|
|
5
6
|
"hatk": "dist/cli.js"
|
|
6
7
|
},
|
|
@@ -19,10 +20,12 @@
|
|
|
19
20
|
"./xrpc-client": "./dist/xrpc-client.js",
|
|
20
21
|
"./views": "./dist/views.js",
|
|
21
22
|
"./seed": "./dist/seed.js",
|
|
23
|
+
"./hooks": "./dist/hooks.js",
|
|
22
24
|
"./setup": "./dist/setup.js",
|
|
23
25
|
"./test": "./dist/test.js",
|
|
24
|
-
"./
|
|
25
|
-
"./vite-plugin": "./dist/vite-plugin.js"
|
|
26
|
+
"./config": "./dist/config.js",
|
|
27
|
+
"./vite-plugin": "./dist/vite-plugin.js",
|
|
28
|
+
"./renderer": "./dist/renderer.js"
|
|
26
29
|
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -33,13 +36,17 @@
|
|
|
33
36
|
"@duckdb/node-api": "^1.4.4-r.1",
|
|
34
37
|
"@hatk/oauth-client": "*",
|
|
35
38
|
"@resvg/resvg-js": "^2.6.2",
|
|
39
|
+
"better-sqlite3": "^12.6.2",
|
|
36
40
|
"satori": "^0.19.2",
|
|
37
41
|
"vitest": "^4",
|
|
38
42
|
"yaml": "^2.7.0"
|
|
39
43
|
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"vite": "^8.0.0"
|
|
46
|
+
},
|
|
40
47
|
"devDependencies": {
|
|
41
|
-
"@
|
|
48
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
42
49
|
"@types/react": "^19.2.14",
|
|
43
|
-
"vite": "^
|
|
50
|
+
"vite": "^8.0.0"
|
|
44
51
|
}
|
|
45
52
|
}
|