commit 6ecfdab4d5b362bdc8272dcdde6c1776c42b39fe
parent bd8fba32906b776cad81fb5165a570be011f2f15
Author: triesap <triesap@radroots.dev>
Date: Sat, 27 Dec 2025 17:39:37 +0000
websql: allow configurable sql.js wasm path resolution
- Add optional sql_wasm_path to WebSqlEngine and WebTangleDatabase configs
- Introduce resolve_wasm_path helper and export via utils index
- Use resolve_wasm_path for sql.js locateFile in client and geocoder
- Default wasm path to /assets/sql-wasm.wasm when unspecified
Diffstat:
10 files changed, 47 insertions(+), 32 deletions(-)
diff --git a/client/src/sql/types.ts b/client/src/sql/types.ts
@@ -28,6 +28,7 @@ export type WebSqlEngineConfig = {
store_key: string;
idb_config: IdbClientConfig;
cipher_config?: IdbClientConfig | null;
+ sql_wasm_path?: string;
};
export interface IClientSqlEncryptedStore {
diff --git a/client/src/sql/web.ts b/client/src/sql/web.ts
@@ -1,4 +1,4 @@
-import { handle_err, type IdbClientConfig, type ResolveError } from "@radroots/utils";
+import { handle_err, resolve_wasm_path, type IdbClientConfig, type ResolveError } from "@radroots/utils";
import { del as idb_del, get as idb_get, set as idb_set, type UseStore } from "idb-keyval";
import type { BindParams, Database, SqlJsStatic, SqlValue, Statement } from "sql.js";
import init_sql_js from "sql.js/dist/sql-wasm.js";
@@ -13,6 +13,7 @@ import { cl_sql_error } from "./error.js";
import type { IClientSqlEncryptedStore, IWebSqlEngine, SqlJsExecOutcome, SqlJsParams, SqlJsResultRow, WebSqlEngineConfig } from "./types.js";
const DEFAULT_SQL_CIPHER_CONFIG: IdbClientConfig = IDB_CONFIG_CIPHER_SQL;
+const DEFAULT_SQL_WASM_PATH = "/assets/sql-wasm.wasm";
const resolve_or_throw = <T>(value: ResolveError<T>): T => {
if (is_error(value)) throw new Error(value.err);
return value;
@@ -99,7 +100,9 @@ export class WebSqlEngine implements IWebSqlEngine {
}
static async create(config: WebSqlEngineConfig): Promise<WebSqlEngine> {
- const sql = await init_sql_js({ locateFile: f => `/assets/${f}` });
+ const sql = await init_sql_js({
+ locateFile: wasm_file => resolve_wasm_path(config.sql_wasm_path, wasm_file, DEFAULT_SQL_WASM_PATH)
+ });
const store = new WebSqlEngineEncryptedStore(config);
const existing = await store.load();
const db = existing ? new sql.Database(existing) : new sql.Database();
diff --git a/client/src/tangle/web.ts b/client/src/tangle/web.ts
@@ -161,6 +161,7 @@ export type WebTangleDatabaseConfig = {
store_key?: string;
idb_config?: IdbClientConfig;
cipher_config?: IdbClientConfig | null;
+ sql_wasm_path?: string;
};
const is_record = (value: unknown): value is Record<string, unknown> =>
@@ -238,12 +239,14 @@ export class WebTangleDatabase implements IWebTangleDatabase {
private readonly store_key: string;
private readonly idb_config: IdbClientConfig;
private readonly cipher_config: IdbClientConfig | null;
+ private readonly sql_wasm_path: string | undefined;
private init_promise: Promise<void> | null = null;
constructor(config?: WebTangleDatabaseConfig) {
this.store_key = config?.store_key ?? DEFAULT_TANGLE_STORE_KEY;
this.idb_config = config?.idb_config ?? DEFAULT_TANGLE_IDB_CONFIG;
this.cipher_config = config?.cipher_config ?? null;
+ this.sql_wasm_path = config?.sql_wasm_path;
}
get_store_key(): string {
@@ -266,7 +269,8 @@ export class WebTangleDatabase implements IWebTangleDatabase {
return {
store_key: this.store_key,
idb_config: this.idb_config,
- cipher_config: this.cipher_config
+ cipher_config: this.cipher_config,
+ sql_wasm_path: this.sql_wasm_path
};
}
diff --git a/geocoder/src/geocoder.ts b/geocoder/src/geocoder.ts
@@ -1,10 +1,11 @@
import type { GeolocationPoint } from "@radroots/geo";
-import { err_msg } from "@radroots/utils";
+import { err_msg, resolve_wasm_path } from "@radroots/utils";
import type { Database } from "sql.js";
import type { GeocoderReverseResult, IGeocoder, IGeocoderConnectResolve, IGeocoderCountryCenter, IGeocoderCountryCenterResolve, IGeocoderCountryListResolve, IGeocoderCountryListResult, IGeocoderCountryResolve, IGeocoderReverseOpts, IGeocoderReverseResolve } from "./types.js";
import { parse_geocode_country_center_result, parse_geocode_country_list_result, parse_geocode_reverse_result } from "./utils.js";
const KM_PER_DEGREE_LATITUDE = 111;
+const DEFAULT_SQL_WASM_PATH = `/assets/sql-wasm.wasm`;
export class Geocoder implements IGeocoder {
private _db: Database | null = null;
@@ -15,11 +16,11 @@ export class Geocoder implements IGeocoder {
this._database_name = database_name || `/geonames/geonames.db`;
}
- public async connect(wasm_dir: string = `/assets`): Promise<IGeocoderConnectResolve> {
+ public async connect(wasm_path?: string): Promise<IGeocoderConnectResolve> {
try {
const init_sqljs = await import(`sql.js`);
const sql = await init_sqljs.default({
- locateFile: wasm_file => `${wasm_dir}/${wasm_file}`
+ locateFile: wasm_file => resolve_wasm_path(wasm_path, wasm_file, DEFAULT_SQL_WASM_PATH)
});
const database_res = await fetch(this._database_name);
const database_buffer = await database_res.arrayBuffer();
diff --git a/geocoder/src/types.ts b/geocoder/src/types.ts
@@ -39,7 +39,7 @@ export type IGeocoderCountryListResolve = ResultsList<IGeocoderCountryListResult
export type IGeocoderCountryCenterResolve = ResultObj<GeolocationPoint> | IError<GeocoderIError>;
export type IGeocoder = {
- connect(wasm_path: string): Promise<IGeocoderConnectResolve>;
+ connect(wasm_path?: string): Promise<IGeocoderConnectResolve>;
reverse(point: GeolocationPoint, opts?: IGeocoderReverseOpts): Promise<IGeocoderReverseResolve>;
country(opts: IGeocoderCountryCenter): Promise<IGeocoderCountryResolve>;
country_list(): Promise<IGeocoderCountryListResolve>;
diff --git a/nostr/src/events/profile/lib.ts b/nostr/src/events/profile/lib.ts
@@ -1,16 +1,16 @@
-import type { RadrootsActorType, RadrootsProfile } from "@radroots/events-bindings";
+import type { RadrootsProfile, RadrootsProfileType } from "@radroots/events-bindings";
import type { NostrEventFigure, NostrSignedEvent } from "../../types/nostr.js";
import { nostr_event_create } from "../lib.js";
-import { tags_profile_actor } from "./tags.js";
+import { tags_profile_type } from "./tags.js";
export const KIND_RADROOTS_PROFILE = 0;
export type KindRadrootsProfile = typeof KIND_RADROOTS_PROFILE;
export const nostr_event_profile = async (
- opts: NostrEventFigure<{ data: RadrootsProfile; actor?: RadrootsActorType }>,
+ opts: NostrEventFigure<{ data: RadrootsProfile; profile_type?: RadrootsProfileType }>,
): Promise<NostrSignedEvent | undefined> => {
- const { data, actor, ...event_opts } = opts;
- const tags = tags_profile_actor(actor);
+ const { data, profile_type, ...event_opts } = opts;
+ const tags = tags_profile_type(profile_type);
return nostr_event_create({
...event_opts,
basis: {
diff --git a/nostr/src/events/profile/parse.ts b/nostr/src/events/profile/parse.ts
@@ -1,14 +1,14 @@
-import type { RadrootsActorType, RadrootsProfile } from "@radroots/events-bindings";
+import type { RadrootsProfile, RadrootsProfileType } from "@radroots/events-bindings";
import { radroots_profile_schema } from "@radroots/events-bindings";
import type { NostrEvent } from "../../types/nostr.js";
import { parse_nostr_event_basis } from "../lib.js";
import type { NostrEventBasis } from "../subscription.js";
import { KIND_RADROOTS_PROFILE, type KindRadrootsProfile } from "./lib.js";
-import { parse_profile_actor_tag } from "./tags.js";
+import { parse_profile_type_tag } from "./tags.js";
export type RadrootsProfileNostrEvent = NostrEventBasis<KindRadrootsProfile> & {
profile: RadrootsProfile;
- actor_type?: RadrootsActorType;
+ profile_type?: RadrootsProfileType;
};
export const parse_nostr_profile_event = (
@@ -19,8 +19,8 @@ export const parse_nostr_profile_event = (
try {
const parsed = JSON.parse(event.content);
const profile = radroots_profile_schema.parse(parsed);
- const actor_type = parse_profile_actor_tag(event.tags);
- return actor_type ? { ...ev, profile, actor_type } : { ...ev, profile };
+ const profile_type = parse_profile_type_tag(event.tags);
+ return profile_type ? { ...ev, profile, profile_type } : { ...ev, profile };
} catch {
return undefined;
}
diff --git a/nostr/src/events/profile/tags.ts b/nostr/src/events/profile/tags.ts
@@ -1,25 +1,24 @@
-import type { RadrootsActorType } from "@radroots/events-bindings";
+import type { RadrootsProfileType } from "@radroots/events-bindings";
import type { NostrEventTags } from "../../types/lib.js";
-const ACTOR_TAG_KEY = "t";
-const ACTOR_TAG_PREFIX = "radroots:actor:";
+const TYPE_TAG_KEY = "t";
+const TYPE_TAG_PREFIX = "radroots:type:";
-const is_actor_type = (value: string): value is RadrootsActorType =>
- value === "person" || value === "farm";
+export const radroots_profile_type_tag_value = (profile_type: RadrootsProfileType): string =>
+ `${TYPE_TAG_PREFIX}${profile_type}`;
-export const radroots_actor_tag_value = (actor: RadrootsActorType): string =>
- `${ACTOR_TAG_PREFIX}${actor}`;
+export const tags_profile_type = (profile_type?: RadrootsProfileType): NostrEventTags =>
+ profile_type ? [[TYPE_TAG_KEY, radroots_profile_type_tag_value(profile_type)]] : [];
-export const tags_profile_actor = (actor?: RadrootsActorType): NostrEventTags =>
- actor ? [[ACTOR_TAG_KEY, radroots_actor_tag_value(actor)]] : [];
-
-export const parse_profile_actor_tag = (tags: NostrEventTags): RadrootsActorType | undefined => {
+export const parse_profile_type_tag = (
+ tags: NostrEventTags,
+): RadrootsProfileType | undefined => {
for (const tag of tags) {
- if (tag[0] !== ACTOR_TAG_KEY) continue;
+ if (tag[0] !== TYPE_TAG_KEY) continue;
const value = tag[1];
- if (!value || !value.startsWith(ACTOR_TAG_PREFIX)) continue;
- const actor = value.slice(ACTOR_TAG_PREFIX.length);
- if (is_actor_type(actor)) return actor;
+ if (!value || !value.startsWith(TYPE_TAG_PREFIX)) continue;
+ const profile_type = value.slice(TYPE_TAG_PREFIX.length);
+ if (profile_type === "individual" || profile_type === "farm") return profile_type;
}
return undefined;
};
diff --git a/utils/src/index.ts b/utils/src/index.ts
@@ -8,6 +8,7 @@ export * from "./media/index.js";
export * from "./model/index.js";
export * from "./numbers/index.js";
export * from "./object/index.js";
+export * from "./path/index.js";
export * from "./text/index.js";
export * from "./time/index.js";
export * from "./types/index.js";
diff --git a/utils/src/path/index.ts b/utils/src/path/index.ts
@@ -0,0 +1,6 @@
+export const resolve_wasm_path = (wasm_path: string | undefined, wasm_file: string, default_wasm_path: string): string => {
+ const resolved_wasm_path = wasm_path ?? default_wasm_path;
+ if (resolved_wasm_path.endsWith(".wasm")) return resolved_wasm_path;
+ const base_path = resolved_wasm_path.endsWith("/") ? resolved_wasm_path.slice(0, -1) : resolved_wasm_path;
+ return `${base_path}/${wasm_file}`;
+};