commit 851e7d76434a287154eda29b96087c50b51842df
parent 503c050534950bc7ea576d2b6ae45f8d11857be0
Author: triesap <tyson@radroots.org>
Date: Thu, 11 Jun 2026 14:14:08 -0700
replica: align libraries with SDK bindings
Diffstat:
41 files changed, 2150 insertions(+), 2771 deletions(-)
diff --git a/apps-lib-pwa/package.json b/apps-lib-pwa/package.json
@@ -82,7 +82,7 @@
"@radroots/events-bindings": "workspace:*",
"@radroots/client": "workspace:*",
"@radroots/geo": "workspace:*",
- "@radroots/tangle-db-schema-bindings": "workspace:*",
+ "@radroots/replica-db-schema-bindings": "workspace:*",
"@radroots/themes": "workspace:*",
"@radroots/utils": "workspace:*",
"@sveltekit-i18n/base": "^1.3.7",
diff --git a/apps-lib-pwa/src/lib/types/app.ts b/apps-lib-pwa/src/lib/types/app.ts
@@ -1,4 +1,4 @@
-import type { TangleDatabaseJsonExport } from "@radroots/client/tangle";
+import type { ReplicaDatabaseJsonExport } from "@radroots/client/replica";
import type { IdbClientConfig } from "@radroots/utils";
export type AppConfigRole = `farm` | `business` | `individual`
@@ -32,7 +32,7 @@ export type LabelFieldKind = `link` | `on` | `shade`;
export type BackupVersions = {
app: string;
- tangle_db: string;
+ replica_db: string;
backup_format: string;
};
@@ -53,7 +53,7 @@ export type ExportedAppState = {
};
database: {
store_key: string;
- backup: TangleDatabaseJsonExport;
+ backup: ReplicaDatabaseJsonExport;
};
};
diff --git a/apps-lib-pwa/src/lib/types/views/farms.ts b/apps-lib-pwa/src/lib/types/views/farms.ts
@@ -1,4 +1,4 @@
-import type { Farm } from "@radroots/tangle-db-schema-bindings";
+import type { Farm } from "@radroots/replica-db-schema-bindings";
import type { GeocoderReverseResult, GeolocationPoint, LocationBasis } from "@radroots/geo";
export type FarmExtended = {
diff --git a/apps-lib-pwa/src/lib/types/views/profile.ts b/apps-lib-pwa/src/lib/types/views/profile.ts
@@ -1,4 +1,4 @@
-import type { NostrProfile } from "@radroots/tangle-db-schema-bindings";
+import type { NostrProfile } from "@radroots/replica-db-schema-bindings";
export type IViewProfileData = {
profile: NostrProfile;
diff --git a/apps-nostr/package.json b/apps-nostr/package.json
@@ -38,9 +38,9 @@
},
"dependencies": {
"@radroots/nostr": "workspace:*",
- "@welshman/app": "workspace:*",
- "@welshman/net": "workspace:*",
- "@welshman/signer": "workspace:*",
- "@welshman/store": "workspace:*"
+ "@welshman/app": "0.8.4",
+ "@welshman/net": "0.8.4",
+ "@welshman/signer": "0.8.4",
+ "@welshman/store": "0.8.4"
}
}
diff --git a/client/package.json b/client/package.json
@@ -70,10 +70,10 @@
"import": "./dist/esm/sql/constants.js",
"require": "./dist/cjs/sql/constants.js"
},
- "./tangle": {
- "types": "./dist/types/tangle/index.d.ts",
- "import": "./dist/esm/tangle/index.js",
- "require": "./dist/cjs/tangle/index.js"
+ "./replica": {
+ "types": "./dist/types/replica/index.d.ts",
+ "import": "./dist/esm/replica/index.js",
+ "require": "./dist/cjs/replica/index.js"
}
},
"scripts": {
@@ -88,9 +88,9 @@
"dependencies": {
"@radroots/geo": "workspace:*",
"@radroots/http": "workspace:*",
- "@radroots/tangle-db-schema-bindings": "workspace:*",
- "@radroots/tangle-db-wasm": "workspace:*",
- "@radroots/tangle-events-wasm": "workspace:*",
+ "@radroots/replica-db-schema-bindings": "workspace:*",
+ "@radroots/replica-db-wasm": "workspace:*",
+ "@radroots/replica-sync-wasm": "workspace:*",
"@radroots/types-bindings": "workspace:*",
"@radroots/utils": "workspace:*",
"@radroots/nostr": "workspace:*",
diff --git a/client/src/error.ts b/client/src/error.ts
@@ -8,4 +8,4 @@ export * from "./keystore/error.js";
export * from "./notifications/error.js";
export * from "./radroots/error.js";
export * from "./sql/error.js";
-export * from "./tangle/error.js";
+export * from "./replica/error.js";
diff --git a/client/src/idb/config.ts b/client/src/idb/config.ts
@@ -8,7 +8,7 @@ export const IDB_STORE_KEYSTORE_NOSTR = "radroots.security.keystore.nostr";
export const IDB_STORE_CRYPTO_REGISTRY = "radroots.security.crypto.registry";
export const IDB_STORE_CIPHER_AES_GCM = "radroots.security.cipher.aes-gcm";
export const IDB_STORE_CIPHER_SQL = "radroots.security.cipher.sql";
-export const IDB_STORE_TANGLE = "radroots.storage.tangle.sql";
+export const IDB_STORE_REPLICA = "radroots.storage.replica.sql";
export const IDB_STORE_CIPHER_SUFFIX = ".cipher";
export const IDB_CONFIG_DATASTORE: IdbClientConfig = {
@@ -41,9 +41,9 @@ export const IDB_CONFIG_CIPHER_SQL: IdbClientConfig = {
store: IDB_STORE_CIPHER_SQL
};
-export const IDB_CONFIG_TANGLE: IdbClientConfig = {
+export const IDB_CONFIG_REPLICA: IdbClientConfig = {
database: RADROOTS_IDB_DATABASE,
- store: IDB_STORE_TANGLE
+ store: IDB_STORE_REPLICA
};
export const RADROOTS_IDB_CONFIGS: IdbClientConfig[] = [
@@ -53,7 +53,7 @@ export const RADROOTS_IDB_CONFIGS: IdbClientConfig[] = [
IDB_CONFIG_CRYPTO_REGISTRY,
IDB_CONFIG_CIPHER_AES_GCM,
IDB_CONFIG_CIPHER_SQL,
- IDB_CONFIG_TANGLE
+ IDB_CONFIG_REPLICA
];
export const RADROOTS_IDB_STORES: string[] = [
diff --git a/client/src/tangle/bridge.ts b/client/src/replica/bridge.ts
diff --git a/client/src/replica/error.ts b/client/src/replica/error.ts
@@ -0,0 +1,10 @@
+export const cl_replica_error = {
+ init_failure: "error.client.replica.init_failure",
+ parse_failure: "error.client.replica.parse_failure",
+ invalid_response: "error.client.replica.invalid_response",
+ runtime_unavailable: "error.client.replica.runtime_unavailable",
+ crypto_unavailable: "error.client.replica.crypto_unavailable"
+} as const;
+
+export type ClientReplicaError = keyof typeof cl_replica_error;
+export type ClientReplicaErrorMessage = (typeof cl_replica_error)[ClientReplicaError];
diff --git a/client/src/tangle/index.ts b/client/src/replica/index.ts
diff --git a/client/src/replica/types.ts b/client/src/replica/types.ts
@@ -0,0 +1,263 @@
+import type {
+ IFarmCreate,
+ IFarmCreateResolve,
+ IFarmDelete,
+ IFarmDeleteResolve,
+ IFarmFindMany,
+ IFarmFindManyResolve,
+ IFarmFindOne,
+ IFarmFindOneResolve,
+ IFarmUpdate,
+ IFarmUpdateResolve,
+ IFarmGcsLocationCreate,
+ IFarmGcsLocationCreateResolve,
+ IFarmGcsLocationDelete,
+ IFarmGcsLocationDeleteResolve,
+ IFarmGcsLocationFindMany,
+ IFarmGcsLocationFindManyResolve,
+ IFarmGcsLocationFindOne,
+ IFarmGcsLocationFindOneResolve,
+ IFarmGcsLocationUpdate,
+ IFarmGcsLocationUpdateResolve,
+ IFarmMemberClaimCreate,
+ IFarmMemberClaimCreateResolve,
+ IFarmMemberClaimDelete,
+ IFarmMemberClaimDeleteResolve,
+ IFarmMemberClaimFindMany,
+ IFarmMemberClaimFindManyResolve,
+ IFarmMemberClaimFindOne,
+ IFarmMemberClaimFindOneResolve,
+ IFarmMemberClaimUpdate,
+ IFarmMemberClaimUpdateResolve,
+ IFarmMemberCreate,
+ IFarmMemberCreateResolve,
+ IFarmMemberDelete,
+ IFarmMemberDeleteResolve,
+ IFarmMemberFindMany,
+ IFarmMemberFindManyResolve,
+ IFarmMemberFindOne,
+ IFarmMemberFindOneResolve,
+ IFarmMemberUpdate,
+ IFarmMemberUpdateResolve,
+ IFarmTagCreate,
+ IFarmTagCreateResolve,
+ IFarmTagDelete,
+ IFarmTagDeleteResolve,
+ IFarmTagFindMany,
+ IFarmTagFindManyResolve,
+ IFarmTagFindOne,
+ IFarmTagFindOneResolve,
+ IFarmTagUpdate,
+ IFarmTagUpdateResolve,
+ IGcsLocationCreate,
+ IGcsLocationCreateResolve,
+ IGcsLocationDelete,
+ IGcsLocationDeleteResolve,
+ IGcsLocationFindMany,
+ IGcsLocationFindManyResolve,
+ IGcsLocationFindOne,
+ IGcsLocationFindOneResolve,
+ IGcsLocationUpdate,
+ IGcsLocationUpdateResolve,
+ ILogErrorCreate,
+ ILogErrorCreateResolve,
+ ILogErrorDelete,
+ ILogErrorDeleteResolve,
+ ILogErrorFindMany,
+ ILogErrorFindManyResolve,
+ ILogErrorFindOne,
+ ILogErrorFindOneResolve,
+ ILogErrorUpdate,
+ ILogErrorUpdateResolve,
+ IMediaImageCreate,
+ IMediaImageCreateResolve,
+ IMediaImageDelete,
+ IMediaImageDeleteResolve,
+ IMediaImageFindMany,
+ IMediaImageFindManyResolve,
+ IMediaImageFindOne,
+ IMediaImageFindOneResolve,
+ IMediaImageUpdate,
+ IMediaImageUpdateResolve,
+ INostrEventStateCreate,
+ INostrEventStateCreateResolve,
+ INostrEventStateDelete,
+ INostrEventStateDeleteResolve,
+ INostrEventStateFindMany,
+ INostrEventStateFindManyResolve,
+ INostrEventStateFindOne,
+ INostrEventStateFindOneResolve,
+ INostrEventStateUpdate,
+ INostrEventStateUpdateResolve,
+ INostrProfileCreate,
+ INostrProfileCreateResolve,
+ INostrProfileDelete,
+ INostrProfileDeleteResolve,
+ INostrProfileFindMany,
+ INostrProfileFindManyResolve,
+ INostrProfileFindOne,
+ INostrProfileFindOneResolve,
+ INostrProfileUpdate,
+ INostrProfileUpdateResolve,
+ INostrRelayCreate,
+ INostrRelayCreateResolve,
+ INostrRelayDelete,
+ INostrRelayDeleteResolve,
+ INostrRelayFindMany,
+ INostrRelayFindManyResolve,
+ INostrRelayFindOne,
+ INostrRelayFindOneResolve,
+ INostrRelayUpdate,
+ INostrRelayUpdateResolve,
+ IPlotCreate,
+ IPlotCreateResolve,
+ IPlotDelete,
+ IPlotDeleteResolve,
+ IPlotFindMany,
+ IPlotFindManyResolve,
+ IPlotFindOne,
+ IPlotFindOneResolve,
+ IPlotGcsLocationCreate,
+ IPlotGcsLocationCreateResolve,
+ IPlotGcsLocationDelete,
+ IPlotGcsLocationDeleteResolve,
+ IPlotGcsLocationFindMany,
+ IPlotGcsLocationFindManyResolve,
+ IPlotGcsLocationFindOne,
+ IPlotGcsLocationFindOneResolve,
+ IPlotGcsLocationUpdate,
+ IPlotGcsLocationUpdateResolve,
+ IPlotTagCreate,
+ IPlotTagCreateResolve,
+ IPlotTagDelete,
+ IPlotTagDeleteResolve,
+ IPlotTagFindMany,
+ IPlotTagFindManyResolve,
+ IPlotTagFindOne,
+ IPlotTagFindOneResolve,
+ IPlotTagUpdate,
+ IPlotTagUpdateResolve,
+ IPlotUpdate,
+ IPlotUpdateResolve,
+ ITradeProductCreate,
+ ITradeProductCreateResolve,
+ ITradeProductDelete,
+ ITradeProductDeleteResolve,
+ ITradeProductFindMany,
+ ITradeProductFindManyResolve,
+ ITradeProductFindOne,
+ ITradeProductFindOneResolve,
+ ITradeProductUpdate,
+ ITradeProductUpdateResolve,
+ INostrProfileRelayRelation,
+ INostrProfileRelayResolve,
+ ITradeProductLocationRelation,
+ ITradeProductLocationResolve,
+ ITradeProductMediaRelation,
+ ITradeProductMediaResolve
+} from "@radroots/replica-db-schema-bindings";
+import { type SqlJsMigrationState } from "../sql/types.js";
+import type { IError } from "@radroots/types-bindings";
+import type {
+ ReplicaDatabaseExportOptions,
+ ReplicaDatabaseJsonExport,
+ ReplicaNostrSyncOptions,
+ ReplicaNostrSyncSummary
+} from "./web.js";
+
+export interface IClientReplicaDatabase {
+ init(): Promise<void>;
+ close(): Promise<void>;
+ migration_state(): Promise<SqlJsMigrationState | IError<string>>;
+ reset(): Promise<SqlJsMigrationState | IError<string>>;
+ reinit(): Promise<SqlJsMigrationState | IError<string>>;
+ get_store_key(): string;
+ export_json(): Promise<ReplicaDatabaseJsonExport | IError<string>>;
+ import_json(backup: ReplicaDatabaseJsonExport): Promise<void | IError<string>>;
+ export_database(opts: ReplicaDatabaseExportOptions): Promise<void | IError<string>>;
+ nostr_sync_all(opts: ReplicaNostrSyncOptions): Promise<ReplicaNostrSyncSummary | IError<string>>;
+ farm_create(opts: IFarmCreate): Promise<IFarmCreateResolve | IError<string>>;
+ farm_find_one(opts: IFarmFindOne): Promise<IFarmFindOneResolve | IError<string>>;
+ farm_find_many(opts?: IFarmFindMany): Promise<IFarmFindManyResolve | IError<string>>;
+ farm_delete(opts: IFarmDelete): Promise<IFarmDeleteResolve | IError<string>>;
+ farm_update(opts: IFarmUpdate): Promise<IFarmUpdateResolve | IError<string>>;
+ plot_create(opts: IPlotCreate): Promise<IPlotCreateResolve | IError<string>>;
+ plot_find_one(opts: IPlotFindOne): Promise<IPlotFindOneResolve | IError<string>>;
+ plot_find_many(opts?: IPlotFindMany): Promise<IPlotFindManyResolve | IError<string>>;
+ plot_delete(opts: IPlotDelete): Promise<IPlotDeleteResolve | IError<string>>;
+ plot_update(opts: IPlotUpdate): Promise<IPlotUpdateResolve | IError<string>>;
+ gcs_location_create(opts: IGcsLocationCreate): Promise<IGcsLocationCreateResolve | IError<string>>;
+ gcs_location_find_one(opts: IGcsLocationFindOne): Promise<IGcsLocationFindOneResolve | IError<string>>;
+ gcs_location_find_many(opts?: IGcsLocationFindMany): Promise<IGcsLocationFindManyResolve | IError<string>>;
+ gcs_location_delete(opts: IGcsLocationDelete): Promise<IGcsLocationDeleteResolve | IError<string>>;
+ gcs_location_update(opts: IGcsLocationUpdate): Promise<IGcsLocationUpdateResolve | IError<string>>;
+ farm_gcs_location_create(opts: IFarmGcsLocationCreate): Promise<IFarmGcsLocationCreateResolve | IError<string>>;
+ farm_gcs_location_find_one(opts: IFarmGcsLocationFindOne): Promise<IFarmGcsLocationFindOneResolve | IError<string>>;
+ farm_gcs_location_find_many(opts?: IFarmGcsLocationFindMany): Promise<IFarmGcsLocationFindManyResolve | IError<string>>;
+ farm_gcs_location_delete(opts: IFarmGcsLocationDelete): Promise<IFarmGcsLocationDeleteResolve | IError<string>>;
+ farm_gcs_location_update(opts: IFarmGcsLocationUpdate): Promise<IFarmGcsLocationUpdateResolve | IError<string>>;
+ plot_gcs_location_create(opts: IPlotGcsLocationCreate): Promise<IPlotGcsLocationCreateResolve | IError<string>>;
+ plot_gcs_location_find_one(opts: IPlotGcsLocationFindOne): Promise<IPlotGcsLocationFindOneResolve | IError<string>>;
+ plot_gcs_location_find_many(opts?: IPlotGcsLocationFindMany): Promise<IPlotGcsLocationFindManyResolve | IError<string>>;
+ plot_gcs_location_delete(opts: IPlotGcsLocationDelete): Promise<IPlotGcsLocationDeleteResolve | IError<string>>;
+ plot_gcs_location_update(opts: IPlotGcsLocationUpdate): Promise<IPlotGcsLocationUpdateResolve | IError<string>>;
+ farm_tag_create(opts: IFarmTagCreate): Promise<IFarmTagCreateResolve | IError<string>>;
+ farm_tag_find_one(opts: IFarmTagFindOne): Promise<IFarmTagFindOneResolve | IError<string>>;
+ farm_tag_find_many(opts?: IFarmTagFindMany): Promise<IFarmTagFindManyResolve | IError<string>>;
+ farm_tag_delete(opts: IFarmTagDelete): Promise<IFarmTagDeleteResolve | IError<string>>;
+ farm_tag_update(opts: IFarmTagUpdate): Promise<IFarmTagUpdateResolve | IError<string>>;
+ plot_tag_create(opts: IPlotTagCreate): Promise<IPlotTagCreateResolve | IError<string>>;
+ plot_tag_find_one(opts: IPlotTagFindOne): Promise<IPlotTagFindOneResolve | IError<string>>;
+ plot_tag_find_many(opts?: IPlotTagFindMany): Promise<IPlotTagFindManyResolve | IError<string>>;
+ plot_tag_delete(opts: IPlotTagDelete): Promise<IPlotTagDeleteResolve | IError<string>>;
+ plot_tag_update(opts: IPlotTagUpdate): Promise<IPlotTagUpdateResolve | IError<string>>;
+ farm_member_create(opts: IFarmMemberCreate): Promise<IFarmMemberCreateResolve | IError<string>>;
+ farm_member_find_one(opts: IFarmMemberFindOne): Promise<IFarmMemberFindOneResolve | IError<string>>;
+ farm_member_find_many(opts?: IFarmMemberFindMany): Promise<IFarmMemberFindManyResolve | IError<string>>;
+ farm_member_delete(opts: IFarmMemberDelete): Promise<IFarmMemberDeleteResolve | IError<string>>;
+ farm_member_update(opts: IFarmMemberUpdate): Promise<IFarmMemberUpdateResolve | IError<string>>;
+ farm_member_claim_create(opts: IFarmMemberClaimCreate): Promise<IFarmMemberClaimCreateResolve | IError<string>>;
+ farm_member_claim_find_one(opts: IFarmMemberClaimFindOne): Promise<IFarmMemberClaimFindOneResolve | IError<string>>;
+ farm_member_claim_find_many(opts?: IFarmMemberClaimFindMany): Promise<IFarmMemberClaimFindManyResolve | IError<string>>;
+ farm_member_claim_delete(opts: IFarmMemberClaimDelete): Promise<IFarmMemberClaimDeleteResolve | IError<string>>;
+ farm_member_claim_update(opts: IFarmMemberClaimUpdate): Promise<IFarmMemberClaimUpdateResolve | IError<string>>;
+ nostr_event_state_create(opts: INostrEventStateCreate): Promise<INostrEventStateCreateResolve | IError<string>>;
+ nostr_event_state_find_one(opts: INostrEventStateFindOne): Promise<INostrEventStateFindOneResolve | IError<string>>;
+ nostr_event_state_find_many(opts?: INostrEventStateFindMany): Promise<INostrEventStateFindManyResolve | IError<string>>;
+ nostr_event_state_delete(opts: INostrEventStateDelete): Promise<INostrEventStateDeleteResolve | IError<string>>;
+ nostr_event_state_update(opts: INostrEventStateUpdate): Promise<INostrEventStateUpdateResolve | IError<string>>;
+ log_error_create(opts: ILogErrorCreate): Promise<ILogErrorCreateResolve | IError<string>>;
+ log_error_find_one(opts: ILogErrorFindOne): Promise<ILogErrorFindOneResolve | IError<string>>;
+ log_error_find_many(opts?: ILogErrorFindMany): Promise<ILogErrorFindManyResolve | IError<string>>;
+ log_error_delete(opts: ILogErrorDelete): Promise<ILogErrorDeleteResolve | IError<string>>;
+ log_error_update(opts: ILogErrorUpdate): Promise<ILogErrorUpdateResolve | IError<string>>;
+ media_image_create(opts: IMediaImageCreate): Promise<IMediaImageCreateResolve | IError<string>>;
+ media_image_find_one(opts: IMediaImageFindOne): Promise<IMediaImageFindOneResolve | IError<string>>;
+ media_image_find_many(opts?: IMediaImageFindMany): Promise<IMediaImageFindManyResolve | IError<string>>;
+ media_image_delete(opts: IMediaImageDelete): Promise<IMediaImageDeleteResolve | IError<string>>;
+ media_image_update(opts: IMediaImageUpdate): Promise<IMediaImageUpdateResolve | IError<string>>;
+ nostr_profile_create(opts: INostrProfileCreate): Promise<INostrProfileCreateResolve | IError<string>>;
+ nostr_profile_find_one(opts: INostrProfileFindOne): Promise<INostrProfileFindOneResolve | IError<string>>;
+ nostr_profile_find_many(opts?: INostrProfileFindMany): Promise<INostrProfileFindManyResolve | IError<string>>;
+ nostr_profile_delete(opts: INostrProfileDelete): Promise<INostrProfileDeleteResolve | IError<string>>;
+ nostr_profile_update(opts: INostrProfileUpdate): Promise<INostrProfileUpdateResolve | IError<string>>;
+ nostr_relay_create(opts: INostrRelayCreate): Promise<INostrRelayCreateResolve | IError<string>>;
+ nostr_relay_find_one(opts: INostrRelayFindOne): Promise<INostrRelayFindOneResolve | IError<string>>;
+ nostr_relay_find_many(opts?: INostrRelayFindMany): Promise<INostrRelayFindManyResolve | IError<string>>;
+ nostr_relay_delete(opts: INostrRelayDelete): Promise<INostrRelayDeleteResolve | IError<string>>;
+ nostr_relay_update(opts: INostrRelayUpdate): Promise<INostrRelayUpdateResolve | IError<string>>;
+ trade_product_create(opts: ITradeProductCreate): Promise<ITradeProductCreateResolve | IError<string>>;
+ trade_product_find_one(opts: ITradeProductFindOne): Promise<ITradeProductFindOneResolve | IError<string>>;
+ trade_product_find_many(opts?: ITradeProductFindMany): Promise<ITradeProductFindManyResolve | IError<string>>;
+ trade_product_delete(opts: ITradeProductDelete): Promise<ITradeProductDeleteResolve | IError<string>>;
+ trade_product_update(opts: ITradeProductUpdate): Promise<ITradeProductUpdateResolve | IError<string>>;
+ nostr_profile_relay_set(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>>;
+ nostr_profile_relay_unset(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>>;
+ trade_product_location_set(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>>;
+ trade_product_location_unset(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>>;
+ trade_product_media_set(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>>;
+ trade_product_media_unset(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>>;
+}
+
+export interface IWebReplicaDatabase extends IClientReplicaDatabase {
+}
diff --git a/client/src/replica/web.ts b/client/src/replica/web.ts
@@ -0,0 +1,1769 @@
+import type {
+ IFarmCreate,
+ IFarmCreateResolve,
+ IFarmDelete,
+ IFarmDeleteResolve,
+ IFarmFindMany,
+ IFarmFindManyResolve,
+ IFarmFindOne,
+ IFarmFindOneResolve,
+ IFarmUpdate,
+ IFarmUpdateResolve,
+ IFarmGcsLocationCreate,
+ IFarmGcsLocationCreateResolve,
+ IFarmGcsLocationDelete,
+ IFarmGcsLocationDeleteResolve,
+ IFarmGcsLocationFindMany,
+ IFarmGcsLocationFindManyResolve,
+ IFarmGcsLocationFindOne,
+ IFarmGcsLocationFindOneResolve,
+ IFarmGcsLocationUpdate,
+ IFarmGcsLocationUpdateResolve,
+ IFarmMemberClaimCreate,
+ IFarmMemberClaimCreateResolve,
+ IFarmMemberClaimDelete,
+ IFarmMemberClaimDeleteResolve,
+ IFarmMemberClaimFindMany,
+ IFarmMemberClaimFindManyResolve,
+ IFarmMemberClaimFindOne,
+ IFarmMemberClaimFindOneResolve,
+ IFarmMemberClaimUpdate,
+ IFarmMemberClaimUpdateResolve,
+ IFarmMemberCreate,
+ IFarmMemberCreateResolve,
+ IFarmMemberDelete,
+ IFarmMemberDeleteResolve,
+ IFarmMemberFindMany,
+ IFarmMemberFindManyResolve,
+ IFarmMemberFindOne,
+ IFarmMemberFindOneResolve,
+ IFarmMemberUpdate,
+ IFarmMemberUpdateResolve,
+ IFarmTagCreate,
+ IFarmTagCreateResolve,
+ IFarmTagDelete,
+ IFarmTagDeleteResolve,
+ IFarmTagFindMany,
+ IFarmTagFindManyResolve,
+ IFarmTagFindOne,
+ IFarmTagFindOneResolve,
+ IFarmTagUpdate,
+ IFarmTagUpdateResolve,
+ IGcsLocationCreate,
+ IGcsLocationCreateResolve,
+ IGcsLocationDelete,
+ IGcsLocationDeleteResolve,
+ IGcsLocationFindMany,
+ IGcsLocationFindManyResolve,
+ IGcsLocationFindOne,
+ IGcsLocationFindOneResolve,
+ IGcsLocationUpdate,
+ IGcsLocationUpdateResolve,
+ ILogErrorCreate,
+ ILogErrorCreateResolve,
+ ILogErrorDelete,
+ ILogErrorDeleteResolve,
+ ILogErrorFindMany,
+ ILogErrorFindManyResolve,
+ ILogErrorFindOne,
+ ILogErrorFindOneResolve,
+ ILogErrorUpdate,
+ ILogErrorUpdateResolve,
+ IMediaImageCreate,
+ IMediaImageCreateResolve,
+ IMediaImageDelete,
+ IMediaImageDeleteResolve,
+ IMediaImageFindMany,
+ IMediaImageFindManyResolve,
+ IMediaImageFindOne,
+ IMediaImageFindOneResolve,
+ IMediaImageUpdate,
+ IMediaImageUpdateResolve,
+ INostrEventStateCreate,
+ INostrEventStateCreateResolve,
+ INostrEventStateDelete,
+ INostrEventStateDeleteResolve,
+ INostrEventStateFindMany,
+ INostrEventStateFindManyResolve,
+ INostrEventStateFindOne,
+ INostrEventStateFindOneResolve,
+ INostrEventStateUpdate,
+ INostrEventStateUpdateResolve,
+ INostrProfileCreate,
+ INostrProfileCreateResolve,
+ INostrProfileDelete,
+ INostrProfileDeleteResolve,
+ INostrProfileFindMany,
+ INostrProfileFindManyResolve,
+ INostrProfileFindOne,
+ INostrProfileFindOneResolve,
+ INostrProfileUpdate,
+ INostrProfileUpdateResolve,
+ INostrRelayCreate,
+ INostrRelayCreateResolve,
+ INostrRelayDelete,
+ INostrRelayDeleteResolve,
+ INostrRelayFindMany,
+ INostrRelayFindManyResolve,
+ INostrRelayFindOne,
+ INostrRelayFindOneResolve,
+ INostrRelayUpdate,
+ INostrRelayUpdateResolve,
+ IPlotCreate,
+ IPlotCreateResolve,
+ IPlotDelete,
+ IPlotDeleteResolve,
+ IPlotFindMany,
+ IPlotFindManyResolve,
+ IPlotFindOne,
+ IPlotFindOneResolve,
+ IPlotGcsLocationCreate,
+ IPlotGcsLocationCreateResolve,
+ IPlotGcsLocationDelete,
+ IPlotGcsLocationDeleteResolve,
+ IPlotGcsLocationFindMany,
+ IPlotGcsLocationFindManyResolve,
+ IPlotGcsLocationFindOne,
+ IPlotGcsLocationFindOneResolve,
+ IPlotGcsLocationUpdate,
+ IPlotGcsLocationUpdateResolve,
+ IPlotTagCreate,
+ IPlotTagCreateResolve,
+ IPlotTagDelete,
+ IPlotTagDeleteResolve,
+ IPlotTagFindMany,
+ IPlotTagFindManyResolve,
+ IPlotTagFindOne,
+ IPlotTagFindOneResolve,
+ IPlotTagUpdate,
+ IPlotTagUpdateResolve,
+ IPlotUpdate,
+ IPlotUpdateResolve,
+ ITradeProductCreate,
+ ITradeProductCreateResolve,
+ ITradeProductDelete,
+ ITradeProductDeleteResolve,
+ ITradeProductFindMany,
+ ITradeProductFindManyResolve,
+ ITradeProductFindOne,
+ ITradeProductFindOneResolve,
+ ITradeProductUpdate,
+ ITradeProductUpdateResolve,
+ INostrProfileRelayRelation,
+ INostrProfileRelayResolve,
+ ITradeProductLocationRelation,
+ ITradeProductLocationResolve,
+ ITradeProductMediaRelation,
+ ITradeProductMediaResolve
+} from "@radroots/replica-db-schema-bindings";
+import init_wasm, {
+ replica_db_farm_create,
+ replica_db_farm_delete,
+ replica_db_farm_find_many,
+ replica_db_farm_find_one,
+ replica_db_farm_update,
+ replica_db_plot_create,
+ replica_db_plot_delete,
+ replica_db_plot_find_many,
+ replica_db_plot_find_one,
+ replica_db_plot_update,
+ replica_db_gcs_location_create,
+ replica_db_gcs_location_delete,
+ replica_db_gcs_location_find_many,
+ replica_db_gcs_location_find_one,
+ replica_db_gcs_location_update,
+ replica_db_farm_gcs_location_create,
+ replica_db_farm_gcs_location_delete,
+ replica_db_farm_gcs_location_find_many,
+ replica_db_farm_gcs_location_find_one,
+ replica_db_farm_gcs_location_update,
+ replica_db_plot_gcs_location_create,
+ replica_db_plot_gcs_location_delete,
+ replica_db_plot_gcs_location_find_many,
+ replica_db_plot_gcs_location_find_one,
+ replica_db_plot_gcs_location_update,
+ replica_db_farm_tag_create,
+ replica_db_farm_tag_delete,
+ replica_db_farm_tag_find_many,
+ replica_db_farm_tag_find_one,
+ replica_db_farm_tag_update,
+ replica_db_plot_tag_create,
+ replica_db_plot_tag_delete,
+ replica_db_plot_tag_find_many,
+ replica_db_plot_tag_find_one,
+ replica_db_plot_tag_update,
+ replica_db_farm_member_create,
+ replica_db_farm_member_delete,
+ replica_db_farm_member_find_many,
+ replica_db_farm_member_find_one,
+ replica_db_farm_member_update,
+ replica_db_farm_member_claim_create,
+ replica_db_farm_member_claim_delete,
+ replica_db_farm_member_claim_find_many,
+ replica_db_farm_member_claim_find_one,
+ replica_db_farm_member_claim_update,
+ replica_db_log_error_create,
+ replica_db_log_error_delete,
+ replica_db_log_error_find_many,
+ replica_db_log_error_find_one,
+ replica_db_log_error_update,
+ replica_db_media_image_create,
+ replica_db_media_image_delete,
+ replica_db_media_image_find_many,
+ replica_db_media_image_find_one,
+ replica_db_media_image_update,
+ replica_db_nostr_event_state_create,
+ replica_db_nostr_event_state_delete,
+ replica_db_nostr_event_state_find_many,
+ replica_db_nostr_event_state_find_one,
+ replica_db_nostr_event_state_update,
+ replica_db_nostr_profile_create,
+ replica_db_nostr_profile_delete,
+ replica_db_nostr_profile_find_many,
+ replica_db_nostr_profile_find_one,
+ replica_db_nostr_profile_update,
+ replica_db_nostr_relay_create,
+ replica_db_nostr_relay_delete,
+ replica_db_nostr_relay_find_many,
+ replica_db_nostr_relay_find_one,
+ replica_db_nostr_relay_update,
+ replica_db_trade_product_create,
+ replica_db_trade_product_delete,
+ replica_db_trade_product_find_many,
+ replica_db_trade_product_find_one,
+ replica_db_trade_product_update,
+ replica_db_nostr_profile_relay_set,
+ replica_db_nostr_profile_relay_unset,
+ replica_db_trade_product_location_set,
+ replica_db_trade_product_location_unset,
+ replica_db_trade_product_media_set,
+ replica_db_trade_product_media_unset,
+ replica_db_reset_database,
+ replica_db_run_migrations,
+ replica_db_export_begin,
+ replica_db_export_finish,
+ replica_db_export_json,
+ replica_db_import_json
+} from "@radroots/replica-db-wasm";
+import init_replica_sync_wasm, {
+ replica_sync_ingest_event,
+ replica_sync_sync_all
+} from "@radroots/replica-sync-wasm";
+import {
+ nostr_context_create,
+ nostr_event_sign,
+ nostr_public_key_from_secret,
+ nostr_publish,
+ nostr_relays_clear,
+ nostr_relays_open,
+ type NostrContext
+} from "@radroots/nostr";
+import type { IError } from "@radroots/types-bindings";
+import { err_msg, handle_err, type IdbClientConfig } from "@radroots/utils";
+import { IDB_CONFIG_REPLICA } from "../idb/config.js";
+import type { SqlJsMigrationRow, SqlJsMigrationState, WebSqlEngineConfig } from "../sql/types.js";
+import { WebSqlEngine } from "../sql/web.js";
+import { radroots_sql_install_bridges } from "./bridge.js";
+import { cl_replica_error } from "./error.js";
+import type { IWebReplicaDatabase } from "./types.js";
+
+export type ReplicaDatabaseSchemaEntry = {
+ object_type: string;
+ name: string;
+ table_name?: string;
+ sql?: string;
+};
+
+export type ReplicaDatabaseMigrationEntry = {
+ name: string;
+ up_sql: string;
+ down_sql: string;
+};
+
+export type ReplicaDatabaseJsonExport = {
+ format_version: string;
+ replica_db_version: string;
+ schema: ReplicaDatabaseSchemaEntry[];
+ data: {
+ name: string;
+ rows: Record<string, unknown>[];
+ }[];
+ migrations: ReplicaDatabaseMigrationEntry[];
+};
+
+export type ReplicaDatabaseExportManifestRs = {
+ export_version: string;
+ replica_db_version: string;
+ backup_format_version: string;
+ schema_hash: string;
+ schema: ReplicaDatabaseSchemaEntry[];
+ migrations: ReplicaDatabaseMigrationEntry[];
+ table_counts: {
+ name: string;
+ row_count: number;
+ }[];
+};
+
+export type NostrEventEnvelope = {
+ id: string;
+ pubkey: string;
+ created_at: number;
+ kind: number;
+ tags: string[][];
+ content: string;
+ sig: string;
+};
+
+export type ReplicaDatabaseExportManifestTs = {
+ app_name: string;
+ app_version: string;
+ exported_at: string;
+ db_sha256: string;
+ db_size_bytes: number;
+ store_key: string;
+ nostr_event?: NostrEventEnvelope;
+};
+
+export type ReplicaDatabaseExportManifest = {
+ rust: ReplicaDatabaseExportManifestRs;
+ client: ReplicaDatabaseExportManifestTs;
+};
+
+export type ReplicaDatabaseExportSnapshot = {
+ manifest_rs: ReplicaDatabaseExportManifestRs;
+ db_bytes: Uint8Array;
+};
+
+export type ReplicaDatabaseExportSignRequest = {
+ db_sha256: string;
+ manifest: ReplicaDatabaseExportManifest;
+};
+
+export type ReplicaDatabaseExportSigner = (opts: ReplicaDatabaseExportSignRequest) => Promise<NostrEventEnvelope | null>;
+
+export type ReplicaDatabaseExportOptions = {
+ app_name: string;
+ app_version: string;
+ store_key?: string;
+ signer?: ReplicaDatabaseExportSigner;
+};
+
+export type ReplicaNostrSyncSigner = {
+ secret_key: string;
+};
+
+export type ReplicaNostrEventDraft = {
+ kind: number;
+ author: string;
+ content: string;
+ tags: string[][];
+};
+
+export type ReplicaNostrSyncBundle = {
+ version: number;
+ events: ReplicaNostrEventDraft[];
+};
+
+export type ReplicaNostrSyncOptions = {
+ relays: string[];
+ signers: ReplicaNostrSyncSigner[];
+ publish_timeout_ms?: number;
+ context?: NostrContext;
+};
+
+export type ReplicaNostrSyncSummary = {
+ events_total: number;
+ events_published: number;
+ events_failed: number;
+ events_skipped: number;
+ missing_signers: string[];
+};
+
+export type WebReplicaDatabaseConfig = {
+ store_key?: string;
+ idb_config?: IdbClientConfig;
+ cipher_config?: IdbClientConfig | null;
+ sql_wasm_path?: string;
+};
+
+const is_record = (value: unknown): value is Record<string, unknown> =>
+ typeof value === "object" && value !== null && !Array.isArray(value);
+
+const is_sql_migration_row = (value: unknown): value is SqlJsMigrationRow => {
+ if (!is_record(value)) return false;
+ return typeof value.id === "number"
+ && Number.isFinite(value.id)
+ && typeof value.name === "string"
+ && typeof value.applied_at === "string";
+};
+
+const is_sql_migration_row_list = (value: unknown): value is SqlJsMigrationRow[] =>
+ Array.isArray(value) && value.every(is_sql_migration_row);
+
+const is_schema_entry = (value: unknown): value is ReplicaDatabaseSchemaEntry => {
+ if (!is_record(value)) return false;
+ if (typeof value.object_type !== "string") return false;
+ if (typeof value.name !== "string") return false;
+ if ("table_name" in value && typeof value.table_name !== "undefined" && typeof value.table_name !== "string") return false;
+ if ("sql" in value && typeof value.sql !== "undefined" && typeof value.sql !== "string") return false;
+ return true;
+};
+
+const is_json_export_data_entry = (value: unknown): value is ReplicaDatabaseJsonExport["data"][number] => {
+ if (!is_record(value)) return false;
+ if (typeof value.name !== "string") return false;
+ if (!Array.isArray(value.rows)) return false;
+ if (!value.rows.every(is_record)) return false;
+ return true;
+};
+
+const is_migration_entry = (value: unknown): value is ReplicaDatabaseMigrationEntry => {
+ if (!is_record(value)) return false;
+ return typeof value.name === "string"
+ && typeof value.up_sql === "string"
+ && typeof value.down_sql === "string";
+};
+
+const is_table_count_entry = (value: unknown): value is ReplicaDatabaseExportManifestRs["table_counts"][number] => {
+ if (!is_record(value)) return false;
+ if (typeof value.name !== "string") return false;
+ if (typeof value.row_count !== "number" || !Number.isFinite(value.row_count)) return false;
+ return true;
+};
+
+const is_replica_database_json_export = (value: unknown): value is ReplicaDatabaseJsonExport => {
+ if (!is_record(value)) return false;
+ if (typeof value.format_version !== "string") return false;
+ if (typeof value.replica_db_version !== "string") return false;
+ if (!Array.isArray(value.schema) || !value.schema.every(is_schema_entry)) return false;
+ if (!Array.isArray(value.data) || !value.data.every(is_json_export_data_entry)) return false;
+ if (!Array.isArray(value.migrations) || !value.migrations.every(is_migration_entry)) return false;
+ return true;
+};
+
+const is_export_manifest_rs = (value: unknown): value is ReplicaDatabaseExportManifestRs => {
+ if (!is_record(value)) return false;
+ if (typeof value.export_version !== "string") return false;
+ if (typeof value.replica_db_version !== "string") return false;
+ if (typeof value.backup_format_version !== "string") return false;
+ if (typeof value.schema_hash !== "string") return false;
+ if (!Array.isArray(value.schema) || !value.schema.every(is_schema_entry)) return false;
+ if (!Array.isArray(value.migrations) || !value.migrations.every(is_migration_entry)) return false;
+ if (!Array.isArray(value.table_counts) || !value.table_counts.every(is_table_count_entry)) return false;
+ return true;
+};
+
+const is_export_snapshot = (value: unknown): value is ReplicaDatabaseExportSnapshot => {
+ if (!is_record(value)) return false;
+ if (!("manifest_rs" in value) || !is_export_manifest_rs(value.manifest_rs)) return false;
+ if (!("db_bytes" in value) || !(value.db_bytes instanceof Uint8Array)) return false;
+ return true;
+};
+
+const is_string_list = (value: unknown): value is string[] =>
+ Array.isArray(value) && value.every((item) => typeof item === "string");
+
+const is_tag_list = (value: unknown): value is string[][] =>
+ Array.isArray(value) && value.every(is_string_list);
+
+const is_replica_nostr_event_draft = (value: unknown): value is ReplicaNostrEventDraft => {
+ if (!is_record(value)) return false;
+ if (typeof value.kind !== "number" || !Number.isFinite(value.kind)) return false;
+ if (typeof value.author !== "string") return false;
+ if (typeof value.content !== "string") return false;
+ if (!is_tag_list(value.tags)) return false;
+ return true;
+};
+
+const is_replica_nostr_sync_bundle = (value: unknown): value is ReplicaNostrSyncBundle => {
+ if (!is_record(value)) return false;
+ if (typeof value.version !== "number" || !Number.isFinite(value.version)) return false;
+ if (!Array.isArray(value.events) || !value.events.every(is_replica_nostr_event_draft)) return false;
+ return true;
+};
+
+const parse_replica_nostr_sync_bundle = (value: unknown): ReplicaNostrSyncBundle | IError<string> => {
+ let parsed: unknown = value;
+ if (typeof value === "string") {
+ try {
+ parsed = JSON.parse(value);
+ } catch {
+ return err_msg(cl_replica_error.parse_failure);
+ }
+ }
+ if (!is_replica_nostr_sync_bundle(parsed)) return err_msg(cl_replica_error.invalid_response);
+ return parsed;
+};
+
+const is_ingest_outcome = (value: unknown): value is "applied" | "skipped" =>
+ value === "applied" || value === "skipped";
+
+const replica_sync_event_d_tag = (tags: string[][]): string => {
+ const match = tags.find((tag) => tag[0] === "d");
+ const value = match?.[1];
+ return typeof value === "string" ? value : "";
+};
+
+const replica_sync_event_key = (draft: ReplicaNostrEventDraft): string =>
+ `${draft.kind}:${draft.author}:${replica_sync_event_d_tag(draft.tags)}`;
+
+const build_signer_map = (signers: ReplicaNostrSyncSigner[]): Record<string, string> => {
+ const map: Record<string, string> = {};
+ for (const signer of signers) {
+ const secret_key = signer.secret_key;
+ if (!secret_key || typeof secret_key !== "string") continue;
+ const pubkey = nostr_public_key_from_secret(secret_key);
+ map[pubkey] = secret_key;
+ }
+ return map;
+};
+
+const publish_results_has_success = (results: Record<string, { status?: string }>): boolean =>
+ Object.values(results).some((result) => result.status === "success");
+
+type ZipEntry = {
+ name: string;
+ data: Uint8Array;
+};
+
+type ZipEntryPrepared = {
+ name_bytes: Uint8Array;
+ data: Uint8Array;
+ crc32: number;
+ size: number;
+};
+
+type ZipFilePickerOptions = {
+ suggestedName?: string;
+ types?: {
+ description?: string;
+ accept: Record<string, string[]>;
+ }[];
+};
+
+type ZipFileHandle = {
+ createWritable(): Promise<ZipFileWritable>;
+};
+
+type ZipFileWritable = {
+ write(data: Uint8Array): Promise<void>;
+ close(): Promise<void>;
+};
+
+type ZipFilePicker = (options?: ZipFilePickerOptions) => Promise<ZipFileHandle>;
+
+const ZIP_CRC_TABLE = (() => {
+ const table = new Uint32Array(256);
+ for (let i = 0; i < 256; i++) {
+ let c = i;
+ for (let k = 0; k < 8; k++) {
+ if (c & 1) c = 0xedb88320 ^ (c >>> 1);
+ else c >>>= 1;
+ }
+ table[i] = c >>> 0;
+ }
+ return table;
+})();
+
+const crc32 = (data: Uint8Array): number => {
+ let crc = 0xffffffff;
+ for (let i = 0; i < data.length; i++) {
+ const idx = (crc ^ data[i]) & 0xff;
+ crc = ZIP_CRC_TABLE[idx] ^ (crc >>> 8);
+ }
+ return (crc ^ 0xffffffff) >>> 0;
+};
+
+const zip_prepare_entries = (entries: ZipEntry[]): ZipEntryPrepared[] => {
+ const enc = new TextEncoder();
+ return entries.map((entry) => ({
+ name_bytes: enc.encode(entry.name),
+ data: entry.data,
+ crc32: crc32(entry.data),
+ size: entry.data.length
+ }));
+};
+
+const zip_dos_time = (date: Date): { time: number; date: number } => {
+ const year = Math.max(1980, date.getFullYear());
+ const month = date.getMonth() + 1;
+ const day = date.getDate();
+ const hours = date.getHours();
+ const minutes = date.getMinutes();
+ const seconds = Math.floor(date.getSeconds() / 2);
+ const time = (hours << 11) | (minutes << 5) | seconds;
+ const date_val = ((year - 1980) << 9) | (month << 5) | day;
+ return { time, date: date_val };
+};
+
+const zip_local_header = (entry: ZipEntryPrepared, time: number, date: number): Uint8Array => {
+ const name_len = entry.name_bytes.length;
+ const buffer = new ArrayBuffer(30 + name_len);
+ const view = new DataView(buffer);
+ view.setUint32(0, 0x04034b50, true);
+ view.setUint16(4, 20, true);
+ view.setUint16(6, 0, true);
+ view.setUint16(8, 0, true);
+ view.setUint16(10, time, true);
+ view.setUint16(12, date, true);
+ view.setUint32(14, entry.crc32, true);
+ view.setUint32(18, entry.size, true);
+ view.setUint32(22, entry.size, true);
+ view.setUint16(26, name_len, true);
+ view.setUint16(28, 0, true);
+ const out = new Uint8Array(buffer);
+ out.set(entry.name_bytes, 30);
+ return out;
+};
+
+const zip_central_header = (
+ entry: ZipEntryPrepared,
+ time: number,
+ date: number,
+ offset: number
+): Uint8Array => {
+ const name_len = entry.name_bytes.length;
+ const buffer = new ArrayBuffer(46 + name_len);
+ const view = new DataView(buffer);
+ view.setUint32(0, 0x02014b50, true);
+ view.setUint16(4, 20, true);
+ view.setUint16(6, 20, true);
+ view.setUint16(8, 0, true);
+ view.setUint16(10, 0, true);
+ view.setUint16(12, time, true);
+ view.setUint16(14, date, true);
+ view.setUint32(16, entry.crc32, true);
+ view.setUint32(20, entry.size, true);
+ view.setUint32(24, entry.size, true);
+ view.setUint16(28, name_len, true);
+ view.setUint16(30, 0, true);
+ view.setUint16(32, 0, true);
+ view.setUint16(34, 0, true);
+ view.setUint16(36, 0, true);
+ view.setUint32(38, 0, true);
+ view.setUint32(42, offset, true);
+ const out = new Uint8Array(buffer);
+ out.set(entry.name_bytes, 46);
+ return out;
+};
+
+const zip_end_record = (entry_count: number, central_size: number, central_offset: number): Uint8Array => {
+ const buffer = new ArrayBuffer(22);
+ const view = new DataView(buffer);
+ view.setUint32(0, 0x06054b50, true);
+ view.setUint16(4, 0, true);
+ view.setUint16(6, 0, true);
+ view.setUint16(8, entry_count, true);
+ view.setUint16(10, entry_count, true);
+ view.setUint32(12, central_size, true);
+ view.setUint32(16, central_offset, true);
+ view.setUint16(20, 0, true);
+ return new Uint8Array(buffer);
+};
+
+const zip_build_bytes = (entries: ZipEntry[]): Uint8Array => {
+ const prepared = zip_prepare_entries(entries);
+ const { time, date } = zip_dos_time(new Date());
+ const local_parts: Uint8Array[] = [];
+ const central_parts: Uint8Array[] = [];
+ let offset = 0;
+
+ for (const entry of prepared) {
+ const local = zip_local_header(entry, time, date);
+ local_parts.push(local, entry.data);
+ const central = zip_central_header(entry, time, date, offset);
+ central_parts.push(central);
+ offset += local.length + entry.data.length;
+ }
+
+ let central_size = 0;
+ for (const part of central_parts) central_size += part.length;
+ const end = zip_end_record(prepared.length, central_size, offset);
+ const total = offset + central_size + end.length;
+ const out = new Uint8Array(total);
+ let cursor = 0;
+ for (const part of local_parts) {
+ out.set(part, cursor);
+ cursor += part.length;
+ }
+ for (const part of central_parts) {
+ out.set(part, cursor);
+ cursor += part.length;
+ }
+ out.set(end, cursor);
+ return out;
+};
+
+const zip_write_stream = async (stream: ZipFileWritable, entries: ZipEntry[]): Promise<void> => {
+ const prepared = zip_prepare_entries(entries);
+ const { time, date } = zip_dos_time(new Date());
+ const central_parts: Uint8Array[] = [];
+ let offset = 0;
+
+ for (const entry of prepared) {
+ const local = zip_local_header(entry, time, date);
+ await stream.write(local);
+ await stream.write(entry.data);
+ central_parts.push(zip_central_header(entry, time, date, offset));
+ offset += local.length + entry.data.length;
+ }
+
+ let central_size = 0;
+ for (const part of central_parts) central_size += part.length;
+ for (const part of central_parts) await stream.write(part);
+ const end = zip_end_record(prepared.length, central_size, offset);
+ await stream.write(end);
+ await stream.close();
+};
+
+type TarEntry = {
+ name: string;
+ data: Uint8Array;
+};
+
+const TAR_BLOCK_SIZE = 512;
+
+const tar_pad_size = (size: number): number => {
+ const rem = size % TAR_BLOCK_SIZE;
+ return rem === 0 ? 0 : TAR_BLOCK_SIZE - rem;
+};
+
+const tar_write_string = (buf: Uint8Array, offset: number, length: number, value: string): void => {
+ const enc = new TextEncoder();
+ const bytes = enc.encode(value);
+ const slice = bytes.length > length ? bytes.slice(0, length) : bytes;
+ buf.set(slice, offset);
+};
+
+const tar_write_octal = (buf: Uint8Array, offset: number, length: number, value: number): void => {
+ const str = value.toString(8).padStart(length - 1, "0");
+ tar_write_string(buf, offset, length - 1, str);
+ buf[offset + length - 1] = 0;
+};
+
+const tar_header = (entry: TarEntry, mtime: number): Uint8Array => {
+ if (entry.name.length > 100) throw new Error("tar entry name too long");
+ const header = new Uint8Array(TAR_BLOCK_SIZE);
+ tar_write_string(header, 0, 100, entry.name);
+ tar_write_octal(header, 100, 8, 0o644);
+ tar_write_octal(header, 108, 8, 0);
+ tar_write_octal(header, 116, 8, 0);
+ tar_write_octal(header, 124, 12, entry.data.length);
+ tar_write_octal(header, 136, 12, mtime);
+ for (let i = 148; i < 156; i++) header[i] = 32;
+ header[156] = 48;
+ tar_write_string(header, 257, 6, "ustar");
+ tar_write_string(header, 263, 2, "00");
+ let checksum = 0;
+ for (let i = 0; i < header.length; i++) checksum += header[i];
+ const chk = checksum.toString(8).padStart(6, "0");
+ tar_write_string(header, 148, 6, chk);
+ header[154] = 0;
+ header[155] = 32;
+ return header;
+};
+
+const tar_build_bytes = (entries: TarEntry[], mtime: number): Uint8Array => {
+ const parts: Uint8Array[] = [];
+ let total = 0;
+ for (const entry of entries) {
+ const header = tar_header(entry, mtime);
+ const pad = tar_pad_size(entry.data.length);
+ parts.push(header, entry.data);
+ total += header.length + entry.data.length;
+ if (pad) {
+ const padding = new Uint8Array(pad);
+ parts.push(padding);
+ total += padding.length;
+ }
+ }
+ const end = new Uint8Array(TAR_BLOCK_SIZE * 2);
+ parts.push(end);
+ total += end.length;
+ const out = new Uint8Array(total);
+ let offset = 0;
+ for (const part of parts) {
+ out.set(part, offset);
+ offset += part.length;
+ }
+ return out;
+};
+
+const tar_stream = (entries: TarEntry[], mtime: number): ReadableStream<Uint8Array> => {
+ return new ReadableStream({
+ start(controller) {
+ for (const entry of entries) {
+ const header = tar_header(entry, mtime);
+ controller.enqueue(header);
+ controller.enqueue(entry.data);
+ const pad = tar_pad_size(entry.data.length);
+ if (pad) controller.enqueue(new Uint8Array(pad));
+ }
+ controller.enqueue(new Uint8Array(TAR_BLOCK_SIZE * 2));
+ controller.close();
+ }
+ });
+};
+
+const gzip_bytes = async (bytes: Uint8Array): Promise<Uint8Array> => {
+ if (typeof CompressionStream === "undefined") {
+ throw new Error("replica export requires gzip support");
+ }
+ const stream = new CompressionStream("gzip");
+ const writer = stream.writable.getWriter();
+ writer.write(bytes);
+ await writer.close();
+ const buffer = await new Response(stream.readable).arrayBuffer();
+ return new Uint8Array(buffer);
+};
+
+const bytes_to_hex = (bytes: Uint8Array): string => {
+ const hex: string[] = [];
+ for (let i = 0; i < bytes.length; i++) {
+ hex.push(bytes[i].toString(16).padStart(2, "0"));
+ }
+ return hex.join("");
+};
+
+const sha256_hex = async (bytes: Uint8Array): Promise<string> => {
+ if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_replica_error.crypto_unavailable);
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
+ return bytes_to_hex(new Uint8Array(digest));
+};
+
+const filename_slug = (value: string): string => {
+ const slug = value
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, "-")
+ .replace(/^-+|-+$/g, "");
+ if (slug.startsWith("radroots-")) return slug;
+ return `radroots-${slug}`;
+};
+
+const export_filename = (app_name: string, app_version: string): string => {
+ const base = filename_slug(app_name);
+ return `${base}-${app_version}-backup.tar.gz`;
+};
+
+const get_zip_file_picker = (): ZipFilePicker | undefined => {
+ if (typeof window === "undefined") return undefined;
+ const picker = (window as Window & { showSaveFilePicker?: ZipFilePicker }).showSaveFilePicker;
+ return picker;
+};
+
+const user_activation_is_active = (): boolean => {
+ if (typeof navigator === "undefined") return false;
+ const nav = navigator as Navigator & { userActivation?: { isActive?: boolean } };
+ if (!nav.userActivation) return true;
+ return nav.userActivation.isActive === true;
+};
+
+const is_permission_error = (err: unknown): boolean => {
+ if (!err) return false;
+ if (typeof err === "string") {
+ const msg = err.toLowerCase();
+ return msg.includes("permission") || msg.includes("denied") || msg.includes("not allowed");
+ }
+ if (!is_record(err)) return false;
+ const name = typeof err.name === "string" ? err.name.toLowerCase() : "";
+ const message = typeof err.message === "string" ? err.message.toLowerCase() : "";
+ if (name.includes("notallowed") || name.includes("abort")) return true;
+ return message.includes("permission") || message.includes("denied") || message.includes("not allowed");
+};
+
+const can_share_file = (file: File): boolean => {
+ if (typeof navigator === "undefined") return false;
+ const nav = navigator as Navigator & { canShare?: (data: { files?: File[] }) => boolean };
+ if (!nav.canShare) return false;
+ return nav.canShare({ files: [file] });
+};
+
+const share_file = async (file: File): Promise<boolean> => {
+ if (typeof navigator === "undefined") return false;
+ const nav = navigator as Navigator & { share?: (data: { files?: File[]; title?: string }) => Promise<void> };
+ if (!nav.share) return false;
+ await nav.share({ files: [file], title: file.name });
+ return true;
+};
+
+const download_blob = (blob: Blob, filename: string): void => {
+ if (typeof document === "undefined") return;
+ const url = URL.createObjectURL(blob);
+ const anchor = document.createElement("a");
+ anchor.href = url;
+ anchor.download = filename;
+ anchor.click();
+ URL.revokeObjectURL(url);
+};
+
+const export_tar_gz = async (filename: string, entries: TarEntry[], mtime: number): Promise<void> => {
+ const picker = get_zip_file_picker();
+ if (picker && user_activation_is_active() && typeof CompressionStream !== "undefined") {
+ try {
+ const handle = await picker({
+ suggestedName: filename,
+ types: [
+ {
+ description: "Radroots replica export",
+ accept: { "application/gzip": [".tar.gz"] }
+ }
+ ]
+ });
+ const stream = await handle.createWritable();
+ const writable_stream = new WritableStream<Uint8Array>({
+ write: (chunk) => stream.write(chunk),
+ close: () => stream.close()
+ });
+ await tar_stream(entries, mtime)
+ .pipeThrough(new CompressionStream("gzip"))
+ .pipeTo(writable_stream);
+ return;
+ } catch (e) {
+ if (!is_permission_error(e)) throw e;
+ }
+ }
+ const tar_bytes = tar_build_bytes(entries, mtime);
+ const gz_bytes = await gzip_bytes(tar_bytes);
+ const blob = new Blob([gz_bytes], { type: "application/gzip" });
+ const file = new File([blob], filename, { type: "application/gzip" });
+ if (can_share_file(file) && user_activation_is_active()) {
+ try {
+ const shared = await share_file(file);
+ if (shared) return;
+ } catch (e) {
+ if (!is_permission_error(e)) throw e;
+ }
+ }
+ download_blob(blob, filename);
+};
+
+const DEFAULT_REPLICA_STORE_KEY = "radroots-pwa-v1-replica-db";
+const DEFAULT_REPLICA_IDB_CONFIG: IdbClientConfig = IDB_CONFIG_REPLICA;
+let wasm_init_promise: Promise<void> | null = null;
+
+const runtime_available = (): boolean => {
+ return typeof window !== "undefined" || typeof self !== "undefined";
+};
+
+const wasm_init_once = async (): Promise<void> => {
+ if (!wasm_init_promise) {
+ wasm_init_promise = (async () => {
+ await init_wasm();
+ await init_replica_sync_wasm();
+ })();
+ }
+ try {
+ await wasm_init_promise;
+ } catch (e) {
+ wasm_init_promise = null;
+ throw e;
+ }
+};
+
+export class WebReplicaDatabase implements IWebReplicaDatabase {
+ private engine: WebSqlEngine | null = null;
+ 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?: WebReplicaDatabaseConfig) {
+ this.store_key = config?.store_key ?? DEFAULT_REPLICA_STORE_KEY;
+ this.idb_config = config?.idb_config ?? DEFAULT_REPLICA_IDB_CONFIG;
+ this.cipher_config = config?.cipher_config ?? null;
+ this.sql_wasm_path = config?.sql_wasm_path;
+ }
+
+ get_store_key(): string {
+ return this.store_key;
+ }
+
+ private serialize<T>(opts: T): string {
+ return JSON.stringify(opts);
+ }
+
+ private deserialize<T>(data: string): T | IError<string> {
+ try {
+ return JSON.parse(data);
+ } catch {
+ return err_msg(cl_replica_error.parse_failure);
+ }
+ }
+
+ private get_engine_config(): WebSqlEngineConfig {
+ return {
+ store_key: this.store_key,
+ idb_config: this.idb_config,
+ cipher_config: this.cipher_config,
+ sql_wasm_path: this.sql_wasm_path
+ };
+ }
+
+ private async ensure_ready(): Promise<void> {
+ await this.init();
+ if (!this.engine) throw new Error(cl_replica_error.init_failure);
+ }
+
+ async init(): Promise<void> {
+ if (this.engine) return;
+ if (!runtime_available()) throw new Error(cl_replica_error.runtime_unavailable);
+ if (!this.init_promise) {
+ this.init_promise = (async () => {
+ await wasm_init_once();
+ this.engine = await WebSqlEngine.create(this.get_engine_config());
+ radroots_sql_install_bridges(this.engine);
+ replica_db_run_migrations();
+ })();
+ }
+ try {
+ await this.init_promise;
+ } catch (e) {
+ this.engine = null;
+ this.init_promise = null;
+ throw e;
+ }
+ }
+
+ async close(): Promise<void> {
+ if (this.engine) await this.engine.close();
+ this.engine = null;
+ this.init_promise = null;
+ }
+
+ async migration_state(): Promise<SqlJsMigrationState | IError<string>> {
+ try {
+ await this.ensure_ready();
+ const parsed = this.engine?.query("select id, name, applied_at from __migrations order by id asc", []) ?? [];
+ if (!is_sql_migration_row_list(parsed)) return err_msg(cl_replica_error.invalid_response);
+ const names = parsed.map((row) => row.name);
+ return { applied_names: names, applied_count: names.length };
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async reset(): Promise<SqlJsMigrationState | IError<string>> {
+ try {
+ await this.ensure_ready();
+ replica_db_reset_database();
+ replica_db_run_migrations();
+ return this.migration_state();
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async reinit(): Promise<SqlJsMigrationState | IError<string>> {
+ try {
+ await this.ensure_ready();
+ if (this.engine) {
+ await this.engine.purge_storage();
+ await this.engine.close();
+ }
+ this.engine = await WebSqlEngine.create(this.get_engine_config());
+ radroots_sql_install_bridges(this.engine);
+ replica_db_run_migrations();
+ return this.migration_state();
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async export_json(): Promise<ReplicaDatabaseJsonExport | IError<string>> {
+ try {
+ await this.ensure_ready();
+ const res = await replica_db_export_json();
+ let parsed: unknown = res;
+ if (typeof res === "string") {
+ try {
+ parsed = JSON.parse(res);
+ } catch {
+ return err_msg(cl_replica_error.parse_failure);
+ }
+ }
+ if (!is_replica_database_json_export(parsed)) return err_msg(cl_replica_error.invalid_response);
+ return parsed;
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async import_json(backup: ReplicaDatabaseJsonExport): Promise<void | IError<string>> {
+ try {
+ await this.ensure_ready();
+ replica_db_import_json(this.serialize(backup));
+ return;
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async export_database(opts: ReplicaDatabaseExportOptions): Promise<void | IError<string>> {
+ try {
+ if (opts.store_key && opts.store_key !== this.store_key) {
+ const alt_db = new WebReplicaDatabase({
+ store_key: opts.store_key,
+ idb_config: this.idb_config,
+ cipher_config: this.cipher_config,
+ sql_wasm_path: this.sql_wasm_path
+ });
+ const res = await alt_db.export_database(opts);
+ await alt_db.close();
+ return res;
+ }
+ await this.export_database_inner(opts);
+ return;
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ async nostr_sync_all(opts: ReplicaNostrSyncOptions): Promise<ReplicaNostrSyncSummary | IError<string>> {
+ try {
+ await this.ensure_ready();
+ const relays = Array.from(new Set(opts.relays.map((relay) => relay.trim()).filter((relay) => relay.length)));
+ if (!relays.length) return err_msg(`replica sync requires relays`);
+ if (!opts.signers.length) return err_msg(`replica sync requires signers`);
+ const signer_map = build_signer_map(opts.signers);
+ if (!Object.keys(signer_map).length) return err_msg(`replica sync requires valid signers`);
+
+ const farms = await this.farm_find_many();
+ if ("err" in farms) return farms;
+ const event_map: Record<string, ReplicaNostrEventDraft> = {};
+ for (const farm of farms.results) {
+ const bundle_raw = replica_sync_sync_all(this.serialize({
+ farm: { id: farm.id },
+ options: null
+ }));
+ const bundle = parse_replica_nostr_sync_bundle(bundle_raw);
+ if ("err" in bundle) return bundle;
+ for (const draft of bundle.events) {
+ const key = replica_sync_event_key(draft);
+ if (!event_map[key]) event_map[key] = draft;
+ }
+ }
+
+ const event_keys = Object.keys(event_map);
+ event_keys.sort();
+ if (!event_keys.length) {
+ return {
+ events_total: 0,
+ events_published: 0,
+ events_failed: 0,
+ events_skipped: 0,
+ missing_signers: []
+ };
+ }
+
+ const context = opts.context ?? nostr_context_create();
+ const context_owned = !opts.context;
+ let events_published = 0;
+ let events_failed = 0;
+ let events_skipped = 0;
+ const missing_signers = new Set<string>();
+
+ try {
+ nostr_relays_open(context, relays);
+ for (const key of event_keys) {
+ const draft = event_map[key];
+ const secret_key = signer_map[draft.author];
+ if (!secret_key) {
+ missing_signers.add(draft.author);
+ events_skipped += 1;
+ continue;
+ }
+ const event = nostr_event_sign({
+ secret_key,
+ event: {
+ kind: draft.kind,
+ created_at: Math.floor(Date.now() / 1000),
+ tags: draft.tags,
+ content: draft.content
+ }
+ });
+ const publish_results = await nostr_publish({
+ event,
+ relays,
+ context,
+ timeout: opts.publish_timeout_ms
+ });
+ if (!publish_results_has_success(publish_results)) {
+ events_failed += 1;
+ continue;
+ }
+ const ingest_result = replica_sync_ingest_event(this.serialize(event));
+ if (!is_ingest_outcome(ingest_result)) {
+ events_failed += 1;
+ continue;
+ }
+ events_published += 1;
+ }
+ } finally {
+ if (context_owned) nostr_relays_clear(context);
+ }
+
+ const summary: ReplicaNostrSyncSummary = {
+ events_total: event_keys.length,
+ events_published,
+ events_failed,
+ events_skipped,
+ missing_signers: Array.from(missing_signers)
+ };
+ if (summary.missing_signers.length) return err_msg(`replica sync missing signers: ${summary.missing_signers.join(", ")}`);
+ if (summary.events_failed) return err_msg(`replica sync publish failed (${summary.events_failed}/${summary.events_total})`);
+ return summary;
+ } catch (e) {
+ return handle_err(e);
+ }
+ }
+
+ private async export_database_inner(opts: ReplicaDatabaseExportOptions): Promise<void> {
+ await this.ensure_ready();
+ const app_name = opts.app_name;
+ const app_version = opts.app_version;
+ const store_key = this.store_key;
+ let export_active = false;
+
+ try {
+ const snapshot_raw = await replica_db_export_begin();
+ export_active = true;
+ if (!is_export_snapshot(snapshot_raw)) throw new Error(cl_replica_error.invalid_response);
+ const manifest_rs = snapshot_raw.manifest_rs;
+ const db_bytes = snapshot_raw.db_bytes;
+ const db_sha256 = await sha256_hex(db_bytes);
+ const exported_at = new Date().toISOString();
+
+ const manifest_ts_base: ReplicaDatabaseExportManifestTs = {
+ app_name,
+ app_version,
+ exported_at,
+ db_sha256,
+ db_size_bytes: db_bytes.byteLength,
+ store_key
+ };
+
+ let manifest: ReplicaDatabaseExportManifest = {
+ rust: manifest_rs,
+ client: manifest_ts_base
+ };
+
+ if (opts.signer) {
+ const nostr_event = await opts.signer({ db_sha256, manifest });
+ if (nostr_event) {
+ manifest = {
+ rust: manifest_rs,
+ client: {
+ ...manifest_ts_base,
+ nostr_event
+ }
+ };
+ }
+ }
+
+ const manifest_json = JSON.stringify(manifest, null, 2);
+ const manifest_bytes = new TextEncoder().encode(manifest_json);
+ const filename = export_filename(app_name, app_version);
+ const mtime = Math.floor(Date.parse(exported_at) / 1000);
+ await export_tar_gz(filename, [
+ { name: "manifest.json", data: manifest_bytes },
+ { name: "replica.db", data: db_bytes }
+ ], mtime);
+ } finally {
+ if (export_active) replica_db_export_finish();
+ }
+ }
+
+ async farm_create(opts: IFarmCreate): Promise<IFarmCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_create(this.serialize(opts));
+ return this.deserialize<IFarmCreateResolve>(res);
+ }
+
+ async farm_find_one(opts: IFarmFindOne): Promise<IFarmFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_find_one(this.serialize(opts));
+ return this.deserialize<IFarmFindOneResolve>(res);
+ }
+
+ async farm_find_many(opts?: IFarmFindMany): Promise<IFarmFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IFarmFindManyResolve>(res);
+ }
+
+ async farm_delete(opts: IFarmDelete): Promise<IFarmDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_delete(this.serialize(opts));
+ return this.deserialize<IFarmDeleteResolve>(res);
+ }
+
+ async farm_update(opts: IFarmUpdate): Promise<IFarmUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_update(this.serialize(opts));
+ return this.deserialize<IFarmUpdateResolve>(res);
+ }
+
+ async plot_create(opts: IPlotCreate): Promise<IPlotCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_create(this.serialize(opts));
+ return this.deserialize<IPlotCreateResolve>(res);
+ }
+
+ async plot_find_one(opts: IPlotFindOne): Promise<IPlotFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_find_one(this.serialize(opts));
+ return this.deserialize<IPlotFindOneResolve>(res);
+ }
+
+ async plot_find_many(opts?: IPlotFindMany): Promise<IPlotFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IPlotFindManyResolve>(res);
+ }
+
+ async plot_delete(opts: IPlotDelete): Promise<IPlotDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_delete(this.serialize(opts));
+ return this.deserialize<IPlotDeleteResolve>(res);
+ }
+
+ async plot_update(opts: IPlotUpdate): Promise<IPlotUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_update(this.serialize(opts));
+ return this.deserialize<IPlotUpdateResolve>(res);
+ }
+
+ async gcs_location_create(opts: IGcsLocationCreate): Promise<IGcsLocationCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_gcs_location_create(this.serialize(opts));
+ return this.deserialize<IGcsLocationCreateResolve>(res);
+ }
+
+ async gcs_location_find_one(opts: IGcsLocationFindOne): Promise<IGcsLocationFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_gcs_location_find_one(this.serialize(opts));
+ return this.deserialize<IGcsLocationFindOneResolve>(res);
+ }
+
+ async gcs_location_find_many(opts?: IGcsLocationFindMany): Promise<IGcsLocationFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_gcs_location_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IGcsLocationFindManyResolve>(res);
+ }
+
+ async gcs_location_delete(opts: IGcsLocationDelete): Promise<IGcsLocationDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_gcs_location_delete(this.serialize(opts));
+ return this.deserialize<IGcsLocationDeleteResolve>(res);
+ }
+
+ async gcs_location_update(opts: IGcsLocationUpdate): Promise<IGcsLocationUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_gcs_location_update(this.serialize(opts));
+ return this.deserialize<IGcsLocationUpdateResolve>(res);
+ }
+
+ async farm_gcs_location_create(opts: IFarmGcsLocationCreate): Promise<IFarmGcsLocationCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_gcs_location_create(this.serialize(opts));
+ return this.deserialize<IFarmGcsLocationCreateResolve>(res);
+ }
+
+ async farm_gcs_location_find_one(opts: IFarmGcsLocationFindOne): Promise<IFarmGcsLocationFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_gcs_location_find_one(this.serialize(opts));
+ return this.deserialize<IFarmGcsLocationFindOneResolve>(res);
+ }
+
+ async farm_gcs_location_find_many(opts?: IFarmGcsLocationFindMany): Promise<IFarmGcsLocationFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_gcs_location_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IFarmGcsLocationFindManyResolve>(res);
+ }
+
+ async farm_gcs_location_delete(opts: IFarmGcsLocationDelete): Promise<IFarmGcsLocationDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_gcs_location_delete(this.serialize(opts));
+ return this.deserialize<IFarmGcsLocationDeleteResolve>(res);
+ }
+
+ async farm_gcs_location_update(opts: IFarmGcsLocationUpdate): Promise<IFarmGcsLocationUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_gcs_location_update(this.serialize(opts));
+ return this.deserialize<IFarmGcsLocationUpdateResolve>(res);
+ }
+
+ async plot_gcs_location_create(opts: IPlotGcsLocationCreate): Promise<IPlotGcsLocationCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_gcs_location_create(this.serialize(opts));
+ return this.deserialize<IPlotGcsLocationCreateResolve>(res);
+ }
+
+ async plot_gcs_location_find_one(opts: IPlotGcsLocationFindOne): Promise<IPlotGcsLocationFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_gcs_location_find_one(this.serialize(opts));
+ return this.deserialize<IPlotGcsLocationFindOneResolve>(res);
+ }
+
+ async plot_gcs_location_find_many(opts?: IPlotGcsLocationFindMany): Promise<IPlotGcsLocationFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_gcs_location_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IPlotGcsLocationFindManyResolve>(res);
+ }
+
+ async plot_gcs_location_delete(opts: IPlotGcsLocationDelete): Promise<IPlotGcsLocationDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_gcs_location_delete(this.serialize(opts));
+ return this.deserialize<IPlotGcsLocationDeleteResolve>(res);
+ }
+
+ async plot_gcs_location_update(opts: IPlotGcsLocationUpdate): Promise<IPlotGcsLocationUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_gcs_location_update(this.serialize(opts));
+ return this.deserialize<IPlotGcsLocationUpdateResolve>(res);
+ }
+
+ async farm_tag_create(opts: IFarmTagCreate): Promise<IFarmTagCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_tag_create(this.serialize(opts));
+ return this.deserialize<IFarmTagCreateResolve>(res);
+ }
+
+ async farm_tag_find_one(opts: IFarmTagFindOne): Promise<IFarmTagFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_tag_find_one(this.serialize(opts));
+ return this.deserialize<IFarmTagFindOneResolve>(res);
+ }
+
+ async farm_tag_find_many(opts?: IFarmTagFindMany): Promise<IFarmTagFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_tag_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IFarmTagFindManyResolve>(res);
+ }
+
+ async farm_tag_delete(opts: IFarmTagDelete): Promise<IFarmTagDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_tag_delete(this.serialize(opts));
+ return this.deserialize<IFarmTagDeleteResolve>(res);
+ }
+
+ async farm_tag_update(opts: IFarmTagUpdate): Promise<IFarmTagUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_tag_update(this.serialize(opts));
+ return this.deserialize<IFarmTagUpdateResolve>(res);
+ }
+
+ async plot_tag_create(opts: IPlotTagCreate): Promise<IPlotTagCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_tag_create(this.serialize(opts));
+ return this.deserialize<IPlotTagCreateResolve>(res);
+ }
+
+ async plot_tag_find_one(opts: IPlotTagFindOne): Promise<IPlotTagFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_tag_find_one(this.serialize(opts));
+ return this.deserialize<IPlotTagFindOneResolve>(res);
+ }
+
+ async plot_tag_find_many(opts?: IPlotTagFindMany): Promise<IPlotTagFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_tag_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IPlotTagFindManyResolve>(res);
+ }
+
+ async plot_tag_delete(opts: IPlotTagDelete): Promise<IPlotTagDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_tag_delete(this.serialize(opts));
+ return this.deserialize<IPlotTagDeleteResolve>(res);
+ }
+
+ async plot_tag_update(opts: IPlotTagUpdate): Promise<IPlotTagUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_plot_tag_update(this.serialize(opts));
+ return this.deserialize<IPlotTagUpdateResolve>(res);
+ }
+
+ async farm_member_create(opts: IFarmMemberCreate): Promise<IFarmMemberCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_create(this.serialize(opts));
+ return this.deserialize<IFarmMemberCreateResolve>(res);
+ }
+
+ async farm_member_find_one(opts: IFarmMemberFindOne): Promise<IFarmMemberFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_find_one(this.serialize(opts));
+ return this.deserialize<IFarmMemberFindOneResolve>(res);
+ }
+
+ async farm_member_find_many(opts?: IFarmMemberFindMany): Promise<IFarmMemberFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IFarmMemberFindManyResolve>(res);
+ }
+
+ async farm_member_delete(opts: IFarmMemberDelete): Promise<IFarmMemberDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_delete(this.serialize(opts));
+ return this.deserialize<IFarmMemberDeleteResolve>(res);
+ }
+
+ async farm_member_update(opts: IFarmMemberUpdate): Promise<IFarmMemberUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_update(this.serialize(opts));
+ return this.deserialize<IFarmMemberUpdateResolve>(res);
+ }
+
+ async farm_member_claim_create(opts: IFarmMemberClaimCreate): Promise<IFarmMemberClaimCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_claim_create(this.serialize(opts));
+ return this.deserialize<IFarmMemberClaimCreateResolve>(res);
+ }
+
+ async farm_member_claim_find_one(opts: IFarmMemberClaimFindOne): Promise<IFarmMemberClaimFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_claim_find_one(this.serialize(opts));
+ return this.deserialize<IFarmMemberClaimFindOneResolve>(res);
+ }
+
+ async farm_member_claim_find_many(opts?: IFarmMemberClaimFindMany): Promise<IFarmMemberClaimFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_claim_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IFarmMemberClaimFindManyResolve>(res);
+ }
+
+ async farm_member_claim_delete(opts: IFarmMemberClaimDelete): Promise<IFarmMemberClaimDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_claim_delete(this.serialize(opts));
+ return this.deserialize<IFarmMemberClaimDeleteResolve>(res);
+ }
+
+ async farm_member_claim_update(opts: IFarmMemberClaimUpdate): Promise<IFarmMemberClaimUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_farm_member_claim_update(this.serialize(opts));
+ return this.deserialize<IFarmMemberClaimUpdateResolve>(res);
+ }
+
+ async log_error_create(opts: ILogErrorCreate): Promise<ILogErrorCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_log_error_create(this.serialize(opts));
+ return this.deserialize<ILogErrorCreateResolve>(res);
+ }
+
+ async log_error_find_one(opts: ILogErrorFindOne): Promise<ILogErrorFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_log_error_find_one(this.serialize(opts));
+ return this.deserialize<ILogErrorFindOneResolve>(res);
+ }
+
+ async log_error_find_many(opts?: ILogErrorFindMany): Promise<ILogErrorFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_log_error_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<ILogErrorFindManyResolve>(res);
+ }
+
+ async log_error_delete(opts: ILogErrorDelete): Promise<ILogErrorDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_log_error_delete(this.serialize(opts));
+ return this.deserialize<ILogErrorDeleteResolve>(res);
+ }
+
+ async log_error_update(opts: ILogErrorUpdate): Promise<ILogErrorUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_log_error_update(this.serialize(opts));
+ return this.deserialize<ILogErrorUpdateResolve>(res);
+ }
+
+ async media_image_create(opts: IMediaImageCreate): Promise<IMediaImageCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_media_image_create(this.serialize(opts));
+ return this.deserialize<IMediaImageCreateResolve>(res);
+ }
+
+ async media_image_find_one(opts: IMediaImageFindOne): Promise<IMediaImageFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_media_image_find_one(this.serialize(opts));
+ return this.deserialize<IMediaImageFindOneResolve>(res);
+ }
+
+ async media_image_find_many(opts?: IMediaImageFindMany): Promise<IMediaImageFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_media_image_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<IMediaImageFindManyResolve>(res);
+ }
+
+ async media_image_delete(opts: IMediaImageDelete): Promise<IMediaImageDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_media_image_delete(this.serialize(opts));
+ return this.deserialize<IMediaImageDeleteResolve>(res);
+ }
+
+ async media_image_update(opts: IMediaImageUpdate): Promise<IMediaImageUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_media_image_update(this.serialize(opts));
+ return this.deserialize<IMediaImageUpdateResolve>(res);
+ }
+
+ async nostr_profile_create(opts: INostrProfileCreate): Promise<INostrProfileCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_create(this.serialize(opts));
+ return this.deserialize<INostrProfileCreateResolve>(res);
+ }
+
+ async nostr_profile_find_one(opts: INostrProfileFindOne): Promise<INostrProfileFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_find_one(this.serialize(opts));
+ return this.deserialize<INostrProfileFindOneResolve>(res);
+ }
+
+ async nostr_profile_find_many(opts?: INostrProfileFindMany): Promise<INostrProfileFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<INostrProfileFindManyResolve>(res);
+ }
+
+ async nostr_profile_delete(opts: INostrProfileDelete): Promise<INostrProfileDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_delete(this.serialize(opts));
+ return this.deserialize<INostrProfileDeleteResolve>(res);
+ }
+
+ async nostr_profile_update(opts: INostrProfileUpdate): Promise<INostrProfileUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_update(this.serialize(opts));
+ return this.deserialize<INostrProfileUpdateResolve>(res);
+ }
+
+ async nostr_event_state_create(opts: INostrEventStateCreate): Promise<INostrEventStateCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_event_state_create(this.serialize(opts));
+ return this.deserialize<INostrEventStateCreateResolve>(res);
+ }
+
+ async nostr_event_state_find_one(opts: INostrEventStateFindOne): Promise<INostrEventStateFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_event_state_find_one(this.serialize(opts));
+ return this.deserialize<INostrEventStateFindOneResolve>(res);
+ }
+
+ async nostr_event_state_find_many(opts?: INostrEventStateFindMany): Promise<INostrEventStateFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_event_state_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<INostrEventStateFindManyResolve>(res);
+ }
+
+ async nostr_event_state_delete(opts: INostrEventStateDelete): Promise<INostrEventStateDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_event_state_delete(this.serialize(opts));
+ return this.deserialize<INostrEventStateDeleteResolve>(res);
+ }
+
+ async nostr_event_state_update(opts: INostrEventStateUpdate): Promise<INostrEventStateUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_event_state_update(this.serialize(opts));
+ return this.deserialize<INostrEventStateUpdateResolve>(res);
+ }
+
+ async nostr_relay_create(opts: INostrRelayCreate): Promise<INostrRelayCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_relay_create(this.serialize(opts));
+ return this.deserialize<INostrRelayCreateResolve>(res);
+ }
+
+ async nostr_relay_find_one(opts: INostrRelayFindOne): Promise<INostrRelayFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_relay_find_one(this.serialize(opts));
+ return this.deserialize<INostrRelayFindOneResolve>(res);
+ }
+
+ async nostr_relay_find_many(opts?: INostrRelayFindMany): Promise<INostrRelayFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_relay_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<INostrRelayFindManyResolve>(res);
+ }
+
+ async nostr_relay_delete(opts: INostrRelayDelete): Promise<INostrRelayDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_relay_delete(this.serialize(opts));
+ return this.deserialize<INostrRelayDeleteResolve>(res);
+ }
+
+ async nostr_relay_update(opts: INostrRelayUpdate): Promise<INostrRelayUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_relay_update(this.serialize(opts));
+ return this.deserialize<INostrRelayUpdateResolve>(res);
+ }
+
+ async trade_product_create(opts: ITradeProductCreate): Promise<ITradeProductCreateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_create(this.serialize(opts));
+ return this.deserialize<ITradeProductCreateResolve>(res);
+ }
+
+ async trade_product_find_one(opts: ITradeProductFindOne): Promise<ITradeProductFindOneResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_find_one(this.serialize(opts));
+ return this.deserialize<ITradeProductFindOneResolve>(res);
+ }
+
+ async trade_product_find_many(opts?: ITradeProductFindMany): Promise<ITradeProductFindManyResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_find_many(this.serialize(opts ?? {}));
+ return this.deserialize<ITradeProductFindManyResolve>(res);
+ }
+
+ async trade_product_delete(opts: ITradeProductDelete): Promise<ITradeProductDeleteResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_delete(this.serialize(opts));
+ return this.deserialize<ITradeProductDeleteResolve>(res);
+ }
+
+ async trade_product_update(opts: ITradeProductUpdate): Promise<ITradeProductUpdateResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_update(this.serialize(opts));
+ return this.deserialize<ITradeProductUpdateResolve>(res);
+ }
+
+ async nostr_profile_relay_set(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_relay_set(this.serialize(opts));
+ return this.deserialize<INostrProfileRelayResolve>(res);
+ }
+
+ async nostr_profile_relay_unset(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_nostr_profile_relay_unset(this.serialize(opts));
+ return this.deserialize<INostrProfileRelayResolve>(res);
+ }
+
+ async trade_product_location_set(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_location_set(this.serialize(opts));
+ return this.deserialize<ITradeProductLocationResolve>(res);
+ }
+
+ async trade_product_location_unset(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_location_unset(this.serialize(opts));
+ return this.deserialize<ITradeProductLocationResolve>(res);
+ }
+
+ async trade_product_media_set(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_media_set(this.serialize(opts));
+ return this.deserialize<ITradeProductMediaResolve>(res);
+ }
+
+ async trade_product_media_unset(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>> {
+ await this.ensure_ready();
+ const res = await replica_db_trade_product_media_unset(this.serialize(opts));
+ return this.deserialize<ITradeProductMediaResolve>(res);
+ }
+
+}
+
+export const web_replica_database_create = async (config?: WebReplicaDatabaseConfig): Promise<WebReplicaDatabase> => {
+ const db = new WebReplicaDatabase(config);
+ await db.init();
+ return db;
+};
diff --git a/client/src/tangle/error.ts b/client/src/tangle/error.ts
@@ -1,10 +0,0 @@
-export const cl_tangle_error = {
- init_failure: "error.client.tangle.init_failure",
- parse_failure: "error.client.tangle.parse_failure",
- invalid_response: "error.client.tangle.invalid_response",
- runtime_unavailable: "error.client.tangle.runtime_unavailable",
- crypto_unavailable: "error.client.tangle.crypto_unavailable"
-} as const;
-
-export type ClientTangleError = keyof typeof cl_tangle_error;
-export type ClientTangleErrorMessage = (typeof cl_tangle_error)[ClientTangleError];
diff --git a/client/src/tangle/types.ts b/client/src/tangle/types.ts
@@ -1,263 +0,0 @@
-import type {
- IFarmCreate,
- IFarmCreateResolve,
- IFarmDelete,
- IFarmDeleteResolve,
- IFarmFindMany,
- IFarmFindManyResolve,
- IFarmFindOne,
- IFarmFindOneResolve,
- IFarmUpdate,
- IFarmUpdateResolve,
- IFarmGcsLocationCreate,
- IFarmGcsLocationCreateResolve,
- IFarmGcsLocationDelete,
- IFarmGcsLocationDeleteResolve,
- IFarmGcsLocationFindMany,
- IFarmGcsLocationFindManyResolve,
- IFarmGcsLocationFindOne,
- IFarmGcsLocationFindOneResolve,
- IFarmGcsLocationUpdate,
- IFarmGcsLocationUpdateResolve,
- IFarmMemberClaimCreate,
- IFarmMemberClaimCreateResolve,
- IFarmMemberClaimDelete,
- IFarmMemberClaimDeleteResolve,
- IFarmMemberClaimFindMany,
- IFarmMemberClaimFindManyResolve,
- IFarmMemberClaimFindOne,
- IFarmMemberClaimFindOneResolve,
- IFarmMemberClaimUpdate,
- IFarmMemberClaimUpdateResolve,
- IFarmMemberCreate,
- IFarmMemberCreateResolve,
- IFarmMemberDelete,
- IFarmMemberDeleteResolve,
- IFarmMemberFindMany,
- IFarmMemberFindManyResolve,
- IFarmMemberFindOne,
- IFarmMemberFindOneResolve,
- IFarmMemberUpdate,
- IFarmMemberUpdateResolve,
- IFarmTagCreate,
- IFarmTagCreateResolve,
- IFarmTagDelete,
- IFarmTagDeleteResolve,
- IFarmTagFindMany,
- IFarmTagFindManyResolve,
- IFarmTagFindOne,
- IFarmTagFindOneResolve,
- IFarmTagUpdate,
- IFarmTagUpdateResolve,
- IGcsLocationCreate,
- IGcsLocationCreateResolve,
- IGcsLocationDelete,
- IGcsLocationDeleteResolve,
- IGcsLocationFindMany,
- IGcsLocationFindManyResolve,
- IGcsLocationFindOne,
- IGcsLocationFindOneResolve,
- IGcsLocationUpdate,
- IGcsLocationUpdateResolve,
- ILogErrorCreate,
- ILogErrorCreateResolve,
- ILogErrorDelete,
- ILogErrorDeleteResolve,
- ILogErrorFindMany,
- ILogErrorFindManyResolve,
- ILogErrorFindOne,
- ILogErrorFindOneResolve,
- ILogErrorUpdate,
- ILogErrorUpdateResolve,
- IMediaImageCreate,
- IMediaImageCreateResolve,
- IMediaImageDelete,
- IMediaImageDeleteResolve,
- IMediaImageFindMany,
- IMediaImageFindManyResolve,
- IMediaImageFindOne,
- IMediaImageFindOneResolve,
- IMediaImageUpdate,
- IMediaImageUpdateResolve,
- INostrEventStateCreate,
- INostrEventStateCreateResolve,
- INostrEventStateDelete,
- INostrEventStateDeleteResolve,
- INostrEventStateFindMany,
- INostrEventStateFindManyResolve,
- INostrEventStateFindOne,
- INostrEventStateFindOneResolve,
- INostrEventStateUpdate,
- INostrEventStateUpdateResolve,
- INostrProfileCreate,
- INostrProfileCreateResolve,
- INostrProfileDelete,
- INostrProfileDeleteResolve,
- INostrProfileFindMany,
- INostrProfileFindManyResolve,
- INostrProfileFindOne,
- INostrProfileFindOneResolve,
- INostrProfileUpdate,
- INostrProfileUpdateResolve,
- INostrRelayCreate,
- INostrRelayCreateResolve,
- INostrRelayDelete,
- INostrRelayDeleteResolve,
- INostrRelayFindMany,
- INostrRelayFindManyResolve,
- INostrRelayFindOne,
- INostrRelayFindOneResolve,
- INostrRelayUpdate,
- INostrRelayUpdateResolve,
- IPlotCreate,
- IPlotCreateResolve,
- IPlotDelete,
- IPlotDeleteResolve,
- IPlotFindMany,
- IPlotFindManyResolve,
- IPlotFindOne,
- IPlotFindOneResolve,
- IPlotGcsLocationCreate,
- IPlotGcsLocationCreateResolve,
- IPlotGcsLocationDelete,
- IPlotGcsLocationDeleteResolve,
- IPlotGcsLocationFindMany,
- IPlotGcsLocationFindManyResolve,
- IPlotGcsLocationFindOne,
- IPlotGcsLocationFindOneResolve,
- IPlotGcsLocationUpdate,
- IPlotGcsLocationUpdateResolve,
- IPlotTagCreate,
- IPlotTagCreateResolve,
- IPlotTagDelete,
- IPlotTagDeleteResolve,
- IPlotTagFindMany,
- IPlotTagFindManyResolve,
- IPlotTagFindOne,
- IPlotTagFindOneResolve,
- IPlotTagUpdate,
- IPlotTagUpdateResolve,
- IPlotUpdate,
- IPlotUpdateResolve,
- ITradeProductCreate,
- ITradeProductCreateResolve,
- ITradeProductDelete,
- ITradeProductDeleteResolve,
- ITradeProductFindMany,
- ITradeProductFindManyResolve,
- ITradeProductFindOne,
- ITradeProductFindOneResolve,
- ITradeProductUpdate,
- ITradeProductUpdateResolve,
- INostrProfileRelayRelation,
- INostrProfileRelayResolve,
- ITradeProductLocationRelation,
- ITradeProductLocationResolve,
- ITradeProductMediaRelation,
- ITradeProductMediaResolve
-} from "@radroots/tangle-db-schema-bindings";
-import { type SqlJsMigrationState } from "../sql/types.js";
-import type { IError } from "@radroots/types-bindings";
-import type {
- TangleDatabaseExportOptions,
- TangleDatabaseJsonExport,
- TangleNostrSyncOptions,
- TangleNostrSyncSummary
-} from "./web.js";
-
-export interface IClientTangleDatabase {
- init(): Promise<void>;
- close(): Promise<void>;
- migration_state(): Promise<SqlJsMigrationState | IError<string>>;
- reset(): Promise<SqlJsMigrationState | IError<string>>;
- reinit(): Promise<SqlJsMigrationState | IError<string>>;
- get_store_key(): string;
- export_json(): Promise<TangleDatabaseJsonExport | IError<string>>;
- import_json(backup: TangleDatabaseJsonExport): Promise<void | IError<string>>;
- export_database(opts: TangleDatabaseExportOptions): Promise<void | IError<string>>;
- nostr_sync_all(opts: TangleNostrSyncOptions): Promise<TangleNostrSyncSummary | IError<string>>;
- farm_create(opts: IFarmCreate): Promise<IFarmCreateResolve | IError<string>>;
- farm_find_one(opts: IFarmFindOne): Promise<IFarmFindOneResolve | IError<string>>;
- farm_find_many(opts?: IFarmFindMany): Promise<IFarmFindManyResolve | IError<string>>;
- farm_delete(opts: IFarmDelete): Promise<IFarmDeleteResolve | IError<string>>;
- farm_update(opts: IFarmUpdate): Promise<IFarmUpdateResolve | IError<string>>;
- plot_create(opts: IPlotCreate): Promise<IPlotCreateResolve | IError<string>>;
- plot_find_one(opts: IPlotFindOne): Promise<IPlotFindOneResolve | IError<string>>;
- plot_find_many(opts?: IPlotFindMany): Promise<IPlotFindManyResolve | IError<string>>;
- plot_delete(opts: IPlotDelete): Promise<IPlotDeleteResolve | IError<string>>;
- plot_update(opts: IPlotUpdate): Promise<IPlotUpdateResolve | IError<string>>;
- gcs_location_create(opts: IGcsLocationCreate): Promise<IGcsLocationCreateResolve | IError<string>>;
- gcs_location_find_one(opts: IGcsLocationFindOne): Promise<IGcsLocationFindOneResolve | IError<string>>;
- gcs_location_find_many(opts?: IGcsLocationFindMany): Promise<IGcsLocationFindManyResolve | IError<string>>;
- gcs_location_delete(opts: IGcsLocationDelete): Promise<IGcsLocationDeleteResolve | IError<string>>;
- gcs_location_update(opts: IGcsLocationUpdate): Promise<IGcsLocationUpdateResolve | IError<string>>;
- farm_gcs_location_create(opts: IFarmGcsLocationCreate): Promise<IFarmGcsLocationCreateResolve | IError<string>>;
- farm_gcs_location_find_one(opts: IFarmGcsLocationFindOne): Promise<IFarmGcsLocationFindOneResolve | IError<string>>;
- farm_gcs_location_find_many(opts?: IFarmGcsLocationFindMany): Promise<IFarmGcsLocationFindManyResolve | IError<string>>;
- farm_gcs_location_delete(opts: IFarmGcsLocationDelete): Promise<IFarmGcsLocationDeleteResolve | IError<string>>;
- farm_gcs_location_update(opts: IFarmGcsLocationUpdate): Promise<IFarmGcsLocationUpdateResolve | IError<string>>;
- plot_gcs_location_create(opts: IPlotGcsLocationCreate): Promise<IPlotGcsLocationCreateResolve | IError<string>>;
- plot_gcs_location_find_one(opts: IPlotGcsLocationFindOne): Promise<IPlotGcsLocationFindOneResolve | IError<string>>;
- plot_gcs_location_find_many(opts?: IPlotGcsLocationFindMany): Promise<IPlotGcsLocationFindManyResolve | IError<string>>;
- plot_gcs_location_delete(opts: IPlotGcsLocationDelete): Promise<IPlotGcsLocationDeleteResolve | IError<string>>;
- plot_gcs_location_update(opts: IPlotGcsLocationUpdate): Promise<IPlotGcsLocationUpdateResolve | IError<string>>;
- farm_tag_create(opts: IFarmTagCreate): Promise<IFarmTagCreateResolve | IError<string>>;
- farm_tag_find_one(opts: IFarmTagFindOne): Promise<IFarmTagFindOneResolve | IError<string>>;
- farm_tag_find_many(opts?: IFarmTagFindMany): Promise<IFarmTagFindManyResolve | IError<string>>;
- farm_tag_delete(opts: IFarmTagDelete): Promise<IFarmTagDeleteResolve | IError<string>>;
- farm_tag_update(opts: IFarmTagUpdate): Promise<IFarmTagUpdateResolve | IError<string>>;
- plot_tag_create(opts: IPlotTagCreate): Promise<IPlotTagCreateResolve | IError<string>>;
- plot_tag_find_one(opts: IPlotTagFindOne): Promise<IPlotTagFindOneResolve | IError<string>>;
- plot_tag_find_many(opts?: IPlotTagFindMany): Promise<IPlotTagFindManyResolve | IError<string>>;
- plot_tag_delete(opts: IPlotTagDelete): Promise<IPlotTagDeleteResolve | IError<string>>;
- plot_tag_update(opts: IPlotTagUpdate): Promise<IPlotTagUpdateResolve | IError<string>>;
- farm_member_create(opts: IFarmMemberCreate): Promise<IFarmMemberCreateResolve | IError<string>>;
- farm_member_find_one(opts: IFarmMemberFindOne): Promise<IFarmMemberFindOneResolve | IError<string>>;
- farm_member_find_many(opts?: IFarmMemberFindMany): Promise<IFarmMemberFindManyResolve | IError<string>>;
- farm_member_delete(opts: IFarmMemberDelete): Promise<IFarmMemberDeleteResolve | IError<string>>;
- farm_member_update(opts: IFarmMemberUpdate): Promise<IFarmMemberUpdateResolve | IError<string>>;
- farm_member_claim_create(opts: IFarmMemberClaimCreate): Promise<IFarmMemberClaimCreateResolve | IError<string>>;
- farm_member_claim_find_one(opts: IFarmMemberClaimFindOne): Promise<IFarmMemberClaimFindOneResolve | IError<string>>;
- farm_member_claim_find_many(opts?: IFarmMemberClaimFindMany): Promise<IFarmMemberClaimFindManyResolve | IError<string>>;
- farm_member_claim_delete(opts: IFarmMemberClaimDelete): Promise<IFarmMemberClaimDeleteResolve | IError<string>>;
- farm_member_claim_update(opts: IFarmMemberClaimUpdate): Promise<IFarmMemberClaimUpdateResolve | IError<string>>;
- nostr_event_state_create(opts: INostrEventStateCreate): Promise<INostrEventStateCreateResolve | IError<string>>;
- nostr_event_state_find_one(opts: INostrEventStateFindOne): Promise<INostrEventStateFindOneResolve | IError<string>>;
- nostr_event_state_find_many(opts?: INostrEventStateFindMany): Promise<INostrEventStateFindManyResolve | IError<string>>;
- nostr_event_state_delete(opts: INostrEventStateDelete): Promise<INostrEventStateDeleteResolve | IError<string>>;
- nostr_event_state_update(opts: INostrEventStateUpdate): Promise<INostrEventStateUpdateResolve | IError<string>>;
- log_error_create(opts: ILogErrorCreate): Promise<ILogErrorCreateResolve | IError<string>>;
- log_error_find_one(opts: ILogErrorFindOne): Promise<ILogErrorFindOneResolve | IError<string>>;
- log_error_find_many(opts?: ILogErrorFindMany): Promise<ILogErrorFindManyResolve | IError<string>>;
- log_error_delete(opts: ILogErrorDelete): Promise<ILogErrorDeleteResolve | IError<string>>;
- log_error_update(opts: ILogErrorUpdate): Promise<ILogErrorUpdateResolve | IError<string>>;
- media_image_create(opts: IMediaImageCreate): Promise<IMediaImageCreateResolve | IError<string>>;
- media_image_find_one(opts: IMediaImageFindOne): Promise<IMediaImageFindOneResolve | IError<string>>;
- media_image_find_many(opts?: IMediaImageFindMany): Promise<IMediaImageFindManyResolve | IError<string>>;
- media_image_delete(opts: IMediaImageDelete): Promise<IMediaImageDeleteResolve | IError<string>>;
- media_image_update(opts: IMediaImageUpdate): Promise<IMediaImageUpdateResolve | IError<string>>;
- nostr_profile_create(opts: INostrProfileCreate): Promise<INostrProfileCreateResolve | IError<string>>;
- nostr_profile_find_one(opts: INostrProfileFindOne): Promise<INostrProfileFindOneResolve | IError<string>>;
- nostr_profile_find_many(opts?: INostrProfileFindMany): Promise<INostrProfileFindManyResolve | IError<string>>;
- nostr_profile_delete(opts: INostrProfileDelete): Promise<INostrProfileDeleteResolve | IError<string>>;
- nostr_profile_update(opts: INostrProfileUpdate): Promise<INostrProfileUpdateResolve | IError<string>>;
- nostr_relay_create(opts: INostrRelayCreate): Promise<INostrRelayCreateResolve | IError<string>>;
- nostr_relay_find_one(opts: INostrRelayFindOne): Promise<INostrRelayFindOneResolve | IError<string>>;
- nostr_relay_find_many(opts?: INostrRelayFindMany): Promise<INostrRelayFindManyResolve | IError<string>>;
- nostr_relay_delete(opts: INostrRelayDelete): Promise<INostrRelayDeleteResolve | IError<string>>;
- nostr_relay_update(opts: INostrRelayUpdate): Promise<INostrRelayUpdateResolve | IError<string>>;
- trade_product_create(opts: ITradeProductCreate): Promise<ITradeProductCreateResolve | IError<string>>;
- trade_product_find_one(opts: ITradeProductFindOne): Promise<ITradeProductFindOneResolve | IError<string>>;
- trade_product_find_many(opts?: ITradeProductFindMany): Promise<ITradeProductFindManyResolve | IError<string>>;
- trade_product_delete(opts: ITradeProductDelete): Promise<ITradeProductDeleteResolve | IError<string>>;
- trade_product_update(opts: ITradeProductUpdate): Promise<ITradeProductUpdateResolve | IError<string>>;
- nostr_profile_relay_set(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>>;
- nostr_profile_relay_unset(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>>;
- trade_product_location_set(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>>;
- trade_product_location_unset(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>>;
- trade_product_media_set(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>>;
- trade_product_media_unset(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>>;
-}
-
-export interface IWebTangleDatabase extends IClientTangleDatabase {
-}
diff --git a/client/src/tangle/web.ts b/client/src/tangle/web.ts
@@ -1,1778 +0,0 @@
-import type {
- IFarmCreate,
- IFarmCreateResolve,
- IFarmDelete,
- IFarmDeleteResolve,
- IFarmFindMany,
- IFarmFindManyResolve,
- IFarmFindOne,
- IFarmFindOneResolve,
- IFarmUpdate,
- IFarmUpdateResolve,
- IFarmGcsLocationCreate,
- IFarmGcsLocationCreateResolve,
- IFarmGcsLocationDelete,
- IFarmGcsLocationDeleteResolve,
- IFarmGcsLocationFindMany,
- IFarmGcsLocationFindManyResolve,
- IFarmGcsLocationFindOne,
- IFarmGcsLocationFindOneResolve,
- IFarmGcsLocationUpdate,
- IFarmGcsLocationUpdateResolve,
- IFarmMemberClaimCreate,
- IFarmMemberClaimCreateResolve,
- IFarmMemberClaimDelete,
- IFarmMemberClaimDeleteResolve,
- IFarmMemberClaimFindMany,
- IFarmMemberClaimFindManyResolve,
- IFarmMemberClaimFindOne,
- IFarmMemberClaimFindOneResolve,
- IFarmMemberClaimUpdate,
- IFarmMemberClaimUpdateResolve,
- IFarmMemberCreate,
- IFarmMemberCreateResolve,
- IFarmMemberDelete,
- IFarmMemberDeleteResolve,
- IFarmMemberFindMany,
- IFarmMemberFindManyResolve,
- IFarmMemberFindOne,
- IFarmMemberFindOneResolve,
- IFarmMemberUpdate,
- IFarmMemberUpdateResolve,
- IFarmTagCreate,
- IFarmTagCreateResolve,
- IFarmTagDelete,
- IFarmTagDeleteResolve,
- IFarmTagFindMany,
- IFarmTagFindManyResolve,
- IFarmTagFindOne,
- IFarmTagFindOneResolve,
- IFarmTagUpdate,
- IFarmTagUpdateResolve,
- IGcsLocationCreate,
- IGcsLocationCreateResolve,
- IGcsLocationDelete,
- IGcsLocationDeleteResolve,
- IGcsLocationFindMany,
- IGcsLocationFindManyResolve,
- IGcsLocationFindOne,
- IGcsLocationFindOneResolve,
- IGcsLocationUpdate,
- IGcsLocationUpdateResolve,
- ILogErrorCreate,
- ILogErrorCreateResolve,
- ILogErrorDelete,
- ILogErrorDeleteResolve,
- ILogErrorFindMany,
- ILogErrorFindManyResolve,
- ILogErrorFindOne,
- ILogErrorFindOneResolve,
- ILogErrorUpdate,
- ILogErrorUpdateResolve,
- IMediaImageCreate,
- IMediaImageCreateResolve,
- IMediaImageDelete,
- IMediaImageDeleteResolve,
- IMediaImageFindMany,
- IMediaImageFindManyResolve,
- IMediaImageFindOne,
- IMediaImageFindOneResolve,
- IMediaImageUpdate,
- IMediaImageUpdateResolve,
- INostrEventStateCreate,
- INostrEventStateCreateResolve,
- INostrEventStateDelete,
- INostrEventStateDeleteResolve,
- INostrEventStateFindMany,
- INostrEventStateFindManyResolve,
- INostrEventStateFindOne,
- INostrEventStateFindOneResolve,
- INostrEventStateUpdate,
- INostrEventStateUpdateResolve,
- INostrProfileCreate,
- INostrProfileCreateResolve,
- INostrProfileDelete,
- INostrProfileDeleteResolve,
- INostrProfileFindMany,
- INostrProfileFindManyResolve,
- INostrProfileFindOne,
- INostrProfileFindOneResolve,
- INostrProfileUpdate,
- INostrProfileUpdateResolve,
- INostrRelayCreate,
- INostrRelayCreateResolve,
- INostrRelayDelete,
- INostrRelayDeleteResolve,
- INostrRelayFindMany,
- INostrRelayFindManyResolve,
- INostrRelayFindOne,
- INostrRelayFindOneResolve,
- INostrRelayUpdate,
- INostrRelayUpdateResolve,
- IPlotCreate,
- IPlotCreateResolve,
- IPlotDelete,
- IPlotDeleteResolve,
- IPlotFindMany,
- IPlotFindManyResolve,
- IPlotFindOne,
- IPlotFindOneResolve,
- IPlotGcsLocationCreate,
- IPlotGcsLocationCreateResolve,
- IPlotGcsLocationDelete,
- IPlotGcsLocationDeleteResolve,
- IPlotGcsLocationFindMany,
- IPlotGcsLocationFindManyResolve,
- IPlotGcsLocationFindOne,
- IPlotGcsLocationFindOneResolve,
- IPlotGcsLocationUpdate,
- IPlotGcsLocationUpdateResolve,
- IPlotTagCreate,
- IPlotTagCreateResolve,
- IPlotTagDelete,
- IPlotTagDeleteResolve,
- IPlotTagFindMany,
- IPlotTagFindManyResolve,
- IPlotTagFindOne,
- IPlotTagFindOneResolve,
- IPlotTagUpdate,
- IPlotTagUpdateResolve,
- IPlotUpdate,
- IPlotUpdateResolve,
- ITradeProductCreate,
- ITradeProductCreateResolve,
- ITradeProductDelete,
- ITradeProductDeleteResolve,
- ITradeProductFindMany,
- ITradeProductFindManyResolve,
- ITradeProductFindOne,
- ITradeProductFindOneResolve,
- ITradeProductUpdate,
- ITradeProductUpdateResolve,
- INostrProfileRelayRelation,
- INostrProfileRelayResolve,
- ITradeProductLocationRelation,
- ITradeProductLocationResolve,
- ITradeProductMediaRelation,
- ITradeProductMediaResolve
-} from "@radroots/tangle-db-schema-bindings";
-import init_wasm, {
- query_sql,
- tangle_db_farm_create,
- tangle_db_farm_delete,
- tangle_db_farm_find_many,
- tangle_db_farm_find_one,
- tangle_db_farm_update,
- tangle_db_plot_create,
- tangle_db_plot_delete,
- tangle_db_plot_find_many,
- tangle_db_plot_find_one,
- tangle_db_plot_update,
- tangle_db_gcs_location_create,
- tangle_db_gcs_location_delete,
- tangle_db_gcs_location_find_many,
- tangle_db_gcs_location_find_one,
- tangle_db_gcs_location_update,
- tangle_db_farm_gcs_location_create,
- tangle_db_farm_gcs_location_delete,
- tangle_db_farm_gcs_location_find_many,
- tangle_db_farm_gcs_location_find_one,
- tangle_db_farm_gcs_location_update,
- tangle_db_plot_gcs_location_create,
- tangle_db_plot_gcs_location_delete,
- tangle_db_plot_gcs_location_find_many,
- tangle_db_plot_gcs_location_find_one,
- tangle_db_plot_gcs_location_update,
- tangle_db_farm_tag_create,
- tangle_db_farm_tag_delete,
- tangle_db_farm_tag_find_many,
- tangle_db_farm_tag_find_one,
- tangle_db_farm_tag_update,
- tangle_db_plot_tag_create,
- tangle_db_plot_tag_delete,
- tangle_db_plot_tag_find_many,
- tangle_db_plot_tag_find_one,
- tangle_db_plot_tag_update,
- tangle_db_farm_member_create,
- tangle_db_farm_member_delete,
- tangle_db_farm_member_find_many,
- tangle_db_farm_member_find_one,
- tangle_db_farm_member_update,
- tangle_db_farm_member_claim_create,
- tangle_db_farm_member_claim_delete,
- tangle_db_farm_member_claim_find_many,
- tangle_db_farm_member_claim_find_one,
- tangle_db_farm_member_claim_update,
- tangle_db_log_error_create,
- tangle_db_log_error_delete,
- tangle_db_log_error_find_many,
- tangle_db_log_error_find_one,
- tangle_db_log_error_update,
- tangle_db_media_image_create,
- tangle_db_media_image_delete,
- tangle_db_media_image_find_many,
- tangle_db_media_image_find_one,
- tangle_db_media_image_update,
- tangle_db_nostr_event_state_create,
- tangle_db_nostr_event_state_delete,
- tangle_db_nostr_event_state_find_many,
- tangle_db_nostr_event_state_find_one,
- tangle_db_nostr_event_state_update,
- tangle_db_nostr_profile_create,
- tangle_db_nostr_profile_delete,
- tangle_db_nostr_profile_find_many,
- tangle_db_nostr_profile_find_one,
- tangle_db_nostr_profile_update,
- tangle_db_nostr_relay_create,
- tangle_db_nostr_relay_delete,
- tangle_db_nostr_relay_find_many,
- tangle_db_nostr_relay_find_one,
- tangle_db_nostr_relay_update,
- tangle_db_trade_product_create,
- tangle_db_trade_product_delete,
- tangle_db_trade_product_find_many,
- tangle_db_trade_product_find_one,
- tangle_db_trade_product_update,
- tangle_db_nostr_profile_relay_set,
- tangle_db_nostr_profile_relay_unset,
- tangle_db_trade_product_location_set,
- tangle_db_trade_product_location_unset,
- tangle_db_trade_product_media_set,
- tangle_db_trade_product_media_unset,
- tangle_db_reset_database,
- tangle_db_run_migrations,
- tangle_db_export_begin,
- tangle_db_export_finish,
- tangle_db_export_json,
- tangle_db_import_json
-} from "@radroots/tangle-db-wasm";
-import init_tangle_events_wasm, {
- tangle_events_ingest_event,
- tangle_events_sync_all
-} from "@radroots/tangle-events-wasm";
-import {
- nostr_context_create,
- nostr_event_sign,
- nostr_public_key_from_secret,
- nostr_publish,
- nostr_relays_clear,
- nostr_relays_open,
- type NostrContext
-} from "@radroots/nostr";
-import type { IError } from "@radroots/types-bindings";
-import { err_msg, handle_err, type IdbClientConfig } from "@radroots/utils";
-import { IDB_CONFIG_TANGLE } from "../idb/config.js";
-import type { SqlJsMigrationRow, SqlJsMigrationState, WebSqlEngineConfig } from "../sql/types.js";
-import { WebSqlEngine } from "../sql/web.js";
-import { radroots_sql_install_bridges } from "./bridge.js";
-import { cl_tangle_error } from "./error.js";
-import type { IWebTangleDatabase } from "./types.js";
-
-export type TangleDatabaseSchemaEntry = {
- object_type: string;
- name: string;
- table_name?: string;
- sql?: string;
-};
-
-export type TangleDatabaseMigrationEntry = {
- name: string;
- up_sql: string;
- down_sql: string;
-};
-
-export type TangleDatabaseJsonExport = {
- format_version: string;
- tangle_db_version: string;
- schema: TangleDatabaseSchemaEntry[];
- data: {
- name: string;
- rows: Record<string, unknown>[];
- }[];
- migrations: TangleDatabaseMigrationEntry[];
-};
-
-export type TangleDatabaseExportManifestRs = {
- export_version: string;
- tangle_db_version: string;
- backup_format_version: string;
- schema_hash: string;
- schema: TangleDatabaseSchemaEntry[];
- migrations: TangleDatabaseMigrationEntry[];
- table_counts: {
- name: string;
- row_count: number;
- }[];
-};
-
-export type NostrEventEnvelope = {
- id: string;
- pubkey: string;
- created_at: number;
- kind: number;
- tags: string[][];
- content: string;
- sig: string;
-};
-
-export type TangleDatabaseExportManifestTs = {
- app_name: string;
- app_version: string;
- exported_at: string;
- db_sha256: string;
- db_size_bytes: number;
- store_key: string;
- nostr_event?: NostrEventEnvelope;
-};
-
-export type TangleDatabaseExportManifest = {
- rust: TangleDatabaseExportManifestRs;
- client: TangleDatabaseExportManifestTs;
-};
-
-export type TangleDatabaseExportSnapshot = {
- manifest_rs: TangleDatabaseExportManifestRs;
- db_bytes: Uint8Array;
-};
-
-export type TangleDatabaseExportSignRequest = {
- db_sha256: string;
- manifest: TangleDatabaseExportManifest;
-};
-
-export type TangleDatabaseExportSigner = (opts: TangleDatabaseExportSignRequest) => Promise<NostrEventEnvelope | null>;
-
-export type TangleDatabaseExportOptions = {
- app_name: string;
- app_version: string;
- store_key?: string;
- signer?: TangleDatabaseExportSigner;
-};
-
-export type TangleNostrSyncSigner = {
- secret_key: string;
-};
-
-export type TangleNostrEventDraft = {
- kind: number;
- author: string;
- content: string;
- tags: string[][];
-};
-
-export type TangleNostrSyncBundle = {
- version: number;
- events: TangleNostrEventDraft[];
-};
-
-export type TangleNostrSyncOptions = {
- relays: string[];
- signers: TangleNostrSyncSigner[];
- publish_timeout_ms?: number;
- context?: NostrContext;
-};
-
-export type TangleNostrSyncSummary = {
- events_total: number;
- events_published: number;
- events_failed: number;
- events_skipped: number;
- missing_signers: string[];
-};
-
-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> =>
- typeof value === "object" && value !== null && !Array.isArray(value);
-
-const is_sql_migration_row = (value: unknown): value is SqlJsMigrationRow => {
- if (!is_record(value)) return false;
- return typeof value.id === "number"
- && Number.isFinite(value.id)
- && typeof value.name === "string"
- && typeof value.applied_at === "string";
-};
-
-const is_sql_migration_row_list = (value: unknown): value is SqlJsMigrationRow[] =>
- Array.isArray(value) && value.every(is_sql_migration_row);
-
-const is_schema_entry = (value: unknown): value is TangleDatabaseSchemaEntry => {
- if (!is_record(value)) return false;
- if (typeof value.object_type !== "string") return false;
- if (typeof value.name !== "string") return false;
- if ("table_name" in value && typeof value.table_name !== "undefined" && typeof value.table_name !== "string") return false;
- if ("sql" in value && typeof value.sql !== "undefined" && typeof value.sql !== "string") return false;
- return true;
-};
-
-const is_json_export_data_entry = (value: unknown): value is TangleDatabaseJsonExport["data"][number] => {
- if (!is_record(value)) return false;
- if (typeof value.name !== "string") return false;
- if (!Array.isArray(value.rows)) return false;
- if (!value.rows.every(is_record)) return false;
- return true;
-};
-
-const is_migration_entry = (value: unknown): value is TangleDatabaseMigrationEntry => {
- if (!is_record(value)) return false;
- return typeof value.name === "string"
- && typeof value.up_sql === "string"
- && typeof value.down_sql === "string";
-};
-
-const is_table_count_entry = (value: unknown): value is TangleDatabaseExportManifestRs["table_counts"][number] => {
- if (!is_record(value)) return false;
- if (typeof value.name !== "string") return false;
- if (typeof value.row_count !== "number" || !Number.isFinite(value.row_count)) return false;
- return true;
-};
-
-const is_tangle_database_json_export = (value: unknown): value is TangleDatabaseJsonExport => {
- if (!is_record(value)) return false;
- if (typeof value.format_version !== "string") return false;
- if (typeof value.tangle_db_version !== "string") return false;
- if (!Array.isArray(value.schema) || !value.schema.every(is_schema_entry)) return false;
- if (!Array.isArray(value.data) || !value.data.every(is_json_export_data_entry)) return false;
- if (!Array.isArray(value.migrations) || !value.migrations.every(is_migration_entry)) return false;
- return true;
-};
-
-const is_export_manifest_rs = (value: unknown): value is TangleDatabaseExportManifestRs => {
- if (!is_record(value)) return false;
- if (typeof value.export_version !== "string") return false;
- if (typeof value.tangle_db_version !== "string") return false;
- if (typeof value.backup_format_version !== "string") return false;
- if (typeof value.schema_hash !== "string") return false;
- if (!Array.isArray(value.schema) || !value.schema.every(is_schema_entry)) return false;
- if (!Array.isArray(value.migrations) || !value.migrations.every(is_migration_entry)) return false;
- if (!Array.isArray(value.table_counts) || !value.table_counts.every(is_table_count_entry)) return false;
- return true;
-};
-
-const is_export_snapshot = (value: unknown): value is TangleDatabaseExportSnapshot => {
- if (!is_record(value)) return false;
- if (!("manifest_rs" in value) || !is_export_manifest_rs(value.manifest_rs)) return false;
- if (!("db_bytes" in value) || !(value.db_bytes instanceof Uint8Array)) return false;
- return true;
-};
-
-const is_string_list = (value: unknown): value is string[] =>
- Array.isArray(value) && value.every((item) => typeof item === "string");
-
-const is_tag_list = (value: unknown): value is string[][] =>
- Array.isArray(value) && value.every(is_string_list);
-
-const is_tangle_nostr_event_draft = (value: unknown): value is TangleNostrEventDraft => {
- if (!is_record(value)) return false;
- if (typeof value.kind !== "number" || !Number.isFinite(value.kind)) return false;
- if (typeof value.author !== "string") return false;
- if (typeof value.content !== "string") return false;
- if (!is_tag_list(value.tags)) return false;
- return true;
-};
-
-const is_tangle_nostr_sync_bundle = (value: unknown): value is TangleNostrSyncBundle => {
- if (!is_record(value)) return false;
- if (typeof value.version !== "number" || !Number.isFinite(value.version)) return false;
- if (!Array.isArray(value.events) || !value.events.every(is_tangle_nostr_event_draft)) return false;
- return true;
-};
-
-const parse_tangle_nostr_sync_bundle = (value: unknown): TangleNostrSyncBundle | IError<string> => {
- let parsed: unknown = value;
- if (typeof value === "string") {
- try {
- parsed = JSON.parse(value);
- } catch {
- return err_msg(cl_tangle_error.parse_failure);
- }
- }
- if (!is_tangle_nostr_sync_bundle(parsed)) return err_msg(cl_tangle_error.invalid_response);
- return parsed;
-};
-
-const is_ingest_outcome = (value: unknown): value is "applied" | "skipped" =>
- value === "applied" || value === "skipped";
-
-const tangle_sync_event_d_tag = (tags: string[][]): string => {
- const match = tags.find((tag) => tag[0] === "d");
- const value = match?.[1];
- return typeof value === "string" ? value : "";
-};
-
-const tangle_sync_event_key = (draft: TangleNostrEventDraft): string =>
- `${draft.kind}:${draft.author}:${tangle_sync_event_d_tag(draft.tags)}`;
-
-const build_signer_map = (signers: TangleNostrSyncSigner[]): Record<string, string> => {
- const map: Record<string, string> = {};
- for (const signer of signers) {
- const secret_key = signer.secret_key;
- if (!secret_key || typeof secret_key !== "string") continue;
- const pubkey = nostr_public_key_from_secret(secret_key);
- map[pubkey] = secret_key;
- }
- return map;
-};
-
-const publish_results_has_success = (results: Record<string, { status?: string }>): boolean =>
- Object.values(results).some((result) => result.status === "success");
-
-type ZipEntry = {
- name: string;
- data: Uint8Array;
-};
-
-type ZipEntryPrepared = {
- name_bytes: Uint8Array;
- data: Uint8Array;
- crc32: number;
- size: number;
-};
-
-type ZipFilePickerOptions = {
- suggestedName?: string;
- types?: {
- description?: string;
- accept: Record<string, string[]>;
- }[];
-};
-
-type ZipFileHandle = {
- createWritable(): Promise<ZipFileWritable>;
-};
-
-type ZipFileWritable = {
- write(data: Uint8Array): Promise<void>;
- close(): Promise<void>;
-};
-
-type ZipFilePicker = (options?: ZipFilePickerOptions) => Promise<ZipFileHandle>;
-
-const ZIP_CRC_TABLE = (() => {
- const table = new Uint32Array(256);
- for (let i = 0; i < 256; i++) {
- let c = i;
- for (let k = 0; k < 8; k++) {
- if (c & 1) c = 0xedb88320 ^ (c >>> 1);
- else c >>>= 1;
- }
- table[i] = c >>> 0;
- }
- return table;
-})();
-
-const crc32 = (data: Uint8Array): number => {
- let crc = 0xffffffff;
- for (let i = 0; i < data.length; i++) {
- const idx = (crc ^ data[i]) & 0xff;
- crc = ZIP_CRC_TABLE[idx] ^ (crc >>> 8);
- }
- return (crc ^ 0xffffffff) >>> 0;
-};
-
-const zip_prepare_entries = (entries: ZipEntry[]): ZipEntryPrepared[] => {
- const enc = new TextEncoder();
- return entries.map((entry) => ({
- name_bytes: enc.encode(entry.name),
- data: entry.data,
- crc32: crc32(entry.data),
- size: entry.data.length
- }));
-};
-
-const zip_dos_time = (date: Date): { time: number; date: number } => {
- const year = Math.max(1980, date.getFullYear());
- const month = date.getMonth() + 1;
- const day = date.getDate();
- const hours = date.getHours();
- const minutes = date.getMinutes();
- const seconds = Math.floor(date.getSeconds() / 2);
- const time = (hours << 11) | (minutes << 5) | seconds;
- const date_val = ((year - 1980) << 9) | (month << 5) | day;
- return { time, date: date_val };
-};
-
-const zip_local_header = (entry: ZipEntryPrepared, time: number, date: number): Uint8Array => {
- const name_len = entry.name_bytes.length;
- const buffer = new ArrayBuffer(30 + name_len);
- const view = new DataView(buffer);
- view.setUint32(0, 0x04034b50, true);
- view.setUint16(4, 20, true);
- view.setUint16(6, 0, true);
- view.setUint16(8, 0, true);
- view.setUint16(10, time, true);
- view.setUint16(12, date, true);
- view.setUint32(14, entry.crc32, true);
- view.setUint32(18, entry.size, true);
- view.setUint32(22, entry.size, true);
- view.setUint16(26, name_len, true);
- view.setUint16(28, 0, true);
- const out = new Uint8Array(buffer);
- out.set(entry.name_bytes, 30);
- return out;
-};
-
-const zip_central_header = (
- entry: ZipEntryPrepared,
- time: number,
- date: number,
- offset: number
-): Uint8Array => {
- const name_len = entry.name_bytes.length;
- const buffer = new ArrayBuffer(46 + name_len);
- const view = new DataView(buffer);
- view.setUint32(0, 0x02014b50, true);
- view.setUint16(4, 20, true);
- view.setUint16(6, 20, true);
- view.setUint16(8, 0, true);
- view.setUint16(10, 0, true);
- view.setUint16(12, time, true);
- view.setUint16(14, date, true);
- view.setUint32(16, entry.crc32, true);
- view.setUint32(20, entry.size, true);
- view.setUint32(24, entry.size, true);
- view.setUint16(28, name_len, true);
- view.setUint16(30, 0, true);
- view.setUint16(32, 0, true);
- view.setUint16(34, 0, true);
- view.setUint16(36, 0, true);
- view.setUint32(38, 0, true);
- view.setUint32(42, offset, true);
- const out = new Uint8Array(buffer);
- out.set(entry.name_bytes, 46);
- return out;
-};
-
-const zip_end_record = (entry_count: number, central_size: number, central_offset: number): Uint8Array => {
- const buffer = new ArrayBuffer(22);
- const view = new DataView(buffer);
- view.setUint32(0, 0x06054b50, true);
- view.setUint16(4, 0, true);
- view.setUint16(6, 0, true);
- view.setUint16(8, entry_count, true);
- view.setUint16(10, entry_count, true);
- view.setUint32(12, central_size, true);
- view.setUint32(16, central_offset, true);
- view.setUint16(20, 0, true);
- return new Uint8Array(buffer);
-};
-
-const zip_build_bytes = (entries: ZipEntry[]): Uint8Array => {
- const prepared = zip_prepare_entries(entries);
- const { time, date } = zip_dos_time(new Date());
- const local_parts: Uint8Array[] = [];
- const central_parts: Uint8Array[] = [];
- let offset = 0;
-
- for (const entry of prepared) {
- const local = zip_local_header(entry, time, date);
- local_parts.push(local, entry.data);
- const central = zip_central_header(entry, time, date, offset);
- central_parts.push(central);
- offset += local.length + entry.data.length;
- }
-
- let central_size = 0;
- for (const part of central_parts) central_size += part.length;
- const end = zip_end_record(prepared.length, central_size, offset);
- const total = offset + central_size + end.length;
- const out = new Uint8Array(total);
- let cursor = 0;
- for (const part of local_parts) {
- out.set(part, cursor);
- cursor += part.length;
- }
- for (const part of central_parts) {
- out.set(part, cursor);
- cursor += part.length;
- }
- out.set(end, cursor);
- return out;
-};
-
-const zip_write_stream = async (stream: ZipFileWritable, entries: ZipEntry[]): Promise<void> => {
- const prepared = zip_prepare_entries(entries);
- const { time, date } = zip_dos_time(new Date());
- const central_parts: Uint8Array[] = [];
- let offset = 0;
-
- for (const entry of prepared) {
- const local = zip_local_header(entry, time, date);
- await stream.write(local);
- await stream.write(entry.data);
- central_parts.push(zip_central_header(entry, time, date, offset));
- offset += local.length + entry.data.length;
- }
-
- let central_size = 0;
- for (const part of central_parts) central_size += part.length;
- for (const part of central_parts) await stream.write(part);
- const end = zip_end_record(prepared.length, central_size, offset);
- await stream.write(end);
- await stream.close();
-};
-
-type TarEntry = {
- name: string;
- data: Uint8Array;
-};
-
-const TAR_BLOCK_SIZE = 512;
-
-const tar_pad_size = (size: number): number => {
- const rem = size % TAR_BLOCK_SIZE;
- return rem === 0 ? 0 : TAR_BLOCK_SIZE - rem;
-};
-
-const tar_write_string = (buf: Uint8Array, offset: number, length: number, value: string): void => {
- const enc = new TextEncoder();
- const bytes = enc.encode(value);
- const slice = bytes.length > length ? bytes.slice(0, length) : bytes;
- buf.set(slice, offset);
-};
-
-const tar_write_octal = (buf: Uint8Array, offset: number, length: number, value: number): void => {
- const str = value.toString(8).padStart(length - 1, "0");
- tar_write_string(buf, offset, length - 1, str);
- buf[offset + length - 1] = 0;
-};
-
-const tar_header = (entry: TarEntry, mtime: number): Uint8Array => {
- if (entry.name.length > 100) throw new Error("tar entry name too long");
- const header = new Uint8Array(TAR_BLOCK_SIZE);
- tar_write_string(header, 0, 100, entry.name);
- tar_write_octal(header, 100, 8, 0o644);
- tar_write_octal(header, 108, 8, 0);
- tar_write_octal(header, 116, 8, 0);
- tar_write_octal(header, 124, 12, entry.data.length);
- tar_write_octal(header, 136, 12, mtime);
- for (let i = 148; i < 156; i++) header[i] = 32;
- header[156] = 48;
- tar_write_string(header, 257, 6, "ustar");
- tar_write_string(header, 263, 2, "00");
- let checksum = 0;
- for (let i = 0; i < header.length; i++) checksum += header[i];
- const chk = checksum.toString(8).padStart(6, "0");
- tar_write_string(header, 148, 6, chk);
- header[154] = 0;
- header[155] = 32;
- return header;
-};
-
-const tar_build_bytes = (entries: TarEntry[], mtime: number): Uint8Array => {
- const parts: Uint8Array[] = [];
- let total = 0;
- for (const entry of entries) {
- const header = tar_header(entry, mtime);
- const pad = tar_pad_size(entry.data.length);
- parts.push(header, entry.data);
- total += header.length + entry.data.length;
- if (pad) {
- const padding = new Uint8Array(pad);
- parts.push(padding);
- total += padding.length;
- }
- }
- const end = new Uint8Array(TAR_BLOCK_SIZE * 2);
- parts.push(end);
- total += end.length;
- const out = new Uint8Array(total);
- let offset = 0;
- for (const part of parts) {
- out.set(part, offset);
- offset += part.length;
- }
- return out;
-};
-
-const tar_stream = (entries: TarEntry[], mtime: number): ReadableStream<Uint8Array> => {
- return new ReadableStream({
- start(controller) {
- for (const entry of entries) {
- const header = tar_header(entry, mtime);
- controller.enqueue(header);
- controller.enqueue(entry.data);
- const pad = tar_pad_size(entry.data.length);
- if (pad) controller.enqueue(new Uint8Array(pad));
- }
- controller.enqueue(new Uint8Array(TAR_BLOCK_SIZE * 2));
- controller.close();
- }
- });
-};
-
-const gzip_bytes = async (bytes: Uint8Array): Promise<Uint8Array> => {
- if (typeof CompressionStream === "undefined") {
- throw new Error("tangle export requires gzip support");
- }
- const stream = new CompressionStream("gzip");
- const writer = stream.writable.getWriter();
- writer.write(bytes);
- await writer.close();
- const buffer = await new Response(stream.readable).arrayBuffer();
- return new Uint8Array(buffer);
-};
-
-const bytes_to_hex = (bytes: Uint8Array): string => {
- const hex: string[] = [];
- for (let i = 0; i < bytes.length; i++) {
- hex.push(bytes[i].toString(16).padStart(2, "0"));
- }
- return hex.join("");
-};
-
-const sha256_hex = async (bytes: Uint8Array): Promise<string> => {
- if (!globalThis.crypto || !globalThis.crypto.subtle) throw new Error(cl_tangle_error.crypto_unavailable);
- const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
- return bytes_to_hex(new Uint8Array(digest));
-};
-
-const filename_slug = (value: string): string => {
- const slug = value
- .toLowerCase()
- .replace(/[^a-z0-9]+/g, "-")
- .replace(/^-+|-+$/g, "");
- if (slug.startsWith("radroots-")) return slug;
- return `radroots-${slug}`;
-};
-
-const export_filename = (app_name: string, app_version: string): string => {
- const base = filename_slug(app_name);
- return `${base}-${app_version}-backup.tar.gz`;
-};
-
-const get_zip_file_picker = (): ZipFilePicker | undefined => {
- if (typeof window === "undefined") return undefined;
- const picker = (window as Window & { showSaveFilePicker?: ZipFilePicker }).showSaveFilePicker;
- return picker;
-};
-
-const user_activation_is_active = (): boolean => {
- if (typeof navigator === "undefined") return false;
- const nav = navigator as Navigator & { userActivation?: { isActive?: boolean } };
- if (!nav.userActivation) return true;
- return nav.userActivation.isActive === true;
-};
-
-const is_permission_error = (err: unknown): boolean => {
- if (!err) return false;
- if (typeof err === "string") {
- const msg = err.toLowerCase();
- return msg.includes("permission") || msg.includes("denied") || msg.includes("not allowed");
- }
- if (!is_record(err)) return false;
- const name = typeof err.name === "string" ? err.name.toLowerCase() : "";
- const message = typeof err.message === "string" ? err.message.toLowerCase() : "";
- if (name.includes("notallowed") || name.includes("abort")) return true;
- return message.includes("permission") || message.includes("denied") || message.includes("not allowed");
-};
-
-const can_share_file = (file: File): boolean => {
- if (typeof navigator === "undefined") return false;
- const nav = navigator as Navigator & { canShare?: (data: { files?: File[] }) => boolean };
- if (!nav.canShare) return false;
- return nav.canShare({ files: [file] });
-};
-
-const share_file = async (file: File): Promise<boolean> => {
- if (typeof navigator === "undefined") return false;
- const nav = navigator as Navigator & { share?: (data: { files?: File[]; title?: string }) => Promise<void> };
- if (!nav.share) return false;
- await nav.share({ files: [file], title: file.name });
- return true;
-};
-
-const download_blob = (blob: Blob, filename: string): void => {
- if (typeof document === "undefined") return;
- const url = URL.createObjectURL(blob);
- const anchor = document.createElement("a");
- anchor.href = url;
- anchor.download = filename;
- anchor.click();
- URL.revokeObjectURL(url);
-};
-
-const export_tar_gz = async (filename: string, entries: TarEntry[], mtime: number): Promise<void> => {
- const picker = get_zip_file_picker();
- if (picker && user_activation_is_active() && typeof CompressionStream !== "undefined") {
- try {
- const handle = await picker({
- suggestedName: filename,
- types: [
- {
- description: "Radroots tangle export",
- accept: { "application/gzip": [".tar.gz"] }
- }
- ]
- });
- const stream = await handle.createWritable();
- const writable_stream = new WritableStream<Uint8Array>({
- write: (chunk) => stream.write(chunk),
- close: () => stream.close()
- });
- await tar_stream(entries, mtime)
- .pipeThrough(new CompressionStream("gzip"))
- .pipeTo(writable_stream);
- return;
- } catch (e) {
- if (!is_permission_error(e)) throw e;
- }
- }
- const tar_bytes = tar_build_bytes(entries, mtime);
- const gz_bytes = await gzip_bytes(tar_bytes);
- const blob = new Blob([gz_bytes], { type: "application/gzip" });
- const file = new File([blob], filename, { type: "application/gzip" });
- if (can_share_file(file) && user_activation_is_active()) {
- try {
- const shared = await share_file(file);
- if (shared) return;
- } catch (e) {
- if (!is_permission_error(e)) throw e;
- }
- }
- download_blob(blob, filename);
-};
-
-const DEFAULT_TANGLE_STORE_KEY = "radroots-pwa-v1-tangle-db";
-const DEFAULT_TANGLE_IDB_CONFIG: IdbClientConfig = IDB_CONFIG_TANGLE;
-let wasm_init_promise: Promise<void> | null = null;
-
-const runtime_available = (): boolean => {
- return typeof window !== "undefined" || typeof self !== "undefined";
-};
-
-const wasm_init_once = async (): Promise<void> => {
- if (!wasm_init_promise) {
- wasm_init_promise = (async () => {
- await init_wasm();
- await init_tangle_events_wasm();
- })();
- }
- try {
- await wasm_init_promise;
- } catch (e) {
- wasm_init_promise = null;
- throw e;
- }
-};
-
-export class WebTangleDatabase implements IWebTangleDatabase {
- private engine: WebSqlEngine | null = null;
- 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 {
- return this.store_key;
- }
-
- private serialize<T>(opts: T): string {
- return JSON.stringify(opts);
- }
-
- private deserialize<T>(data: string): T | IError<string> {
- try {
- return JSON.parse(data);
- } catch {
- return err_msg(cl_tangle_error.parse_failure);
- }
- }
-
- private get_engine_config(): WebSqlEngineConfig {
- return {
- store_key: this.store_key,
- idb_config: this.idb_config,
- cipher_config: this.cipher_config,
- sql_wasm_path: this.sql_wasm_path
- };
- }
-
- private async ensure_ready(): Promise<void> {
- await this.init();
- if (!this.engine) throw new Error(cl_tangle_error.init_failure);
- }
-
- async init(): Promise<void> {
- if (this.engine) return;
- if (!runtime_available()) throw new Error(cl_tangle_error.runtime_unavailable);
- if (!this.init_promise) {
- this.init_promise = (async () => {
- await wasm_init_once();
- this.engine = await WebSqlEngine.create(this.get_engine_config());
- radroots_sql_install_bridges(this.engine);
- tangle_db_run_migrations();
- })();
- }
- try {
- await this.init_promise;
- } catch (e) {
- this.engine = null;
- this.init_promise = null;
- throw e;
- }
- }
-
- async close(): Promise<void> {
- if (this.engine) await this.engine.close();
- this.engine = null;
- this.init_promise = null;
- }
-
- async migration_state(): Promise<SqlJsMigrationState | IError<string>> {
- try {
- await this.ensure_ready();
- const res = await query_sql("select id, name, applied_at from __migrations order by id asc", "[]");
- let parsed: unknown = res;
- if (typeof res === "string") {
- try {
- parsed = JSON.parse(res);
- } catch {
- return err_msg(cl_tangle_error.parse_failure);
- }
- }
- if (!is_sql_migration_row_list(parsed)) return err_msg(cl_tangle_error.invalid_response);
- const names = parsed.map((row) => row.name);
- return { applied_names: names, applied_count: names.length };
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async reset(): Promise<SqlJsMigrationState | IError<string>> {
- try {
- await this.ensure_ready();
- tangle_db_reset_database();
- tangle_db_run_migrations();
- return this.migration_state();
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async reinit(): Promise<SqlJsMigrationState | IError<string>> {
- try {
- await this.ensure_ready();
- if (this.engine) {
- await this.engine.purge_storage();
- await this.engine.close();
- }
- this.engine = await WebSqlEngine.create(this.get_engine_config());
- radroots_sql_install_bridges(this.engine);
- tangle_db_run_migrations();
- return this.migration_state();
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async export_json(): Promise<TangleDatabaseJsonExport | IError<string>> {
- try {
- await this.ensure_ready();
- const res = await tangle_db_export_json();
- let parsed: unknown = res;
- if (typeof res === "string") {
- try {
- parsed = JSON.parse(res);
- } catch {
- return err_msg(cl_tangle_error.parse_failure);
- }
- }
- if (!is_tangle_database_json_export(parsed)) return err_msg(cl_tangle_error.invalid_response);
- return parsed;
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async import_json(backup: TangleDatabaseJsonExport): Promise<void | IError<string>> {
- try {
- await this.ensure_ready();
- tangle_db_import_json(this.serialize(backup));
- return;
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async export_database(opts: TangleDatabaseExportOptions): Promise<void | IError<string>> {
- try {
- if (opts.store_key && opts.store_key !== this.store_key) {
- const alt_db = new WebTangleDatabase({
- store_key: opts.store_key,
- idb_config: this.idb_config,
- cipher_config: this.cipher_config,
- sql_wasm_path: this.sql_wasm_path
- });
- const res = await alt_db.export_database(opts);
- await alt_db.close();
- return res;
- }
- await this.export_database_inner(opts);
- return;
- } catch (e) {
- return handle_err(e);
- }
- }
-
- async nostr_sync_all(opts: TangleNostrSyncOptions): Promise<TangleNostrSyncSummary | IError<string>> {
- try {
- await this.ensure_ready();
- const relays = Array.from(new Set(opts.relays.map((relay) => relay.trim()).filter((relay) => relay.length)));
- if (!relays.length) return err_msg(`tangle sync requires relays`);
- if (!opts.signers.length) return err_msg(`tangle sync requires signers`);
- const signer_map = build_signer_map(opts.signers);
- if (!Object.keys(signer_map).length) return err_msg(`tangle sync requires valid signers`);
-
- const farms = await this.farm_find_many();
- if ("err" in farms) return farms;
- const event_map: Record<string, TangleNostrEventDraft> = {};
- for (const farm of farms.results) {
- const bundle_raw = tangle_events_sync_all(this.serialize({
- farm: { id: farm.id },
- options: null
- }));
- const bundle = parse_tangle_nostr_sync_bundle(bundle_raw);
- if ("err" in bundle) return bundle;
- for (const draft of bundle.events) {
- const key = tangle_sync_event_key(draft);
- if (!event_map[key]) event_map[key] = draft;
- }
- }
-
- const event_keys = Object.keys(event_map);
- event_keys.sort();
- if (!event_keys.length) {
- return {
- events_total: 0,
- events_published: 0,
- events_failed: 0,
- events_skipped: 0,
- missing_signers: []
- };
- }
-
- const context = opts.context ?? nostr_context_create();
- const context_owned = !opts.context;
- let events_published = 0;
- let events_failed = 0;
- let events_skipped = 0;
- const missing_signers = new Set<string>();
-
- try {
- nostr_relays_open(context, relays);
- for (const key of event_keys) {
- const draft = event_map[key];
- const secret_key = signer_map[draft.author];
- if (!secret_key) {
- missing_signers.add(draft.author);
- events_skipped += 1;
- continue;
- }
- const event = nostr_event_sign({
- secret_key,
- event: {
- kind: draft.kind,
- created_at: Math.floor(Date.now() / 1000),
- tags: draft.tags,
- content: draft.content
- }
- });
- const publish_results = await nostr_publish({
- event,
- relays,
- context,
- timeout: opts.publish_timeout_ms
- });
- if (!publish_results_has_success(publish_results)) {
- events_failed += 1;
- continue;
- }
- const ingest_result = tangle_events_ingest_event(this.serialize(event));
- if (!is_ingest_outcome(ingest_result)) {
- events_failed += 1;
- continue;
- }
- events_published += 1;
- }
- } finally {
- if (context_owned) nostr_relays_clear(context);
- }
-
- const summary: TangleNostrSyncSummary = {
- events_total: event_keys.length,
- events_published,
- events_failed,
- events_skipped,
- missing_signers: Array.from(missing_signers)
- };
- if (summary.missing_signers.length) return err_msg(`tangle sync missing signers: ${summary.missing_signers.join(", ")}`);
- if (summary.events_failed) return err_msg(`tangle sync publish failed (${summary.events_failed}/${summary.events_total})`);
- return summary;
- } catch (e) {
- return handle_err(e);
- }
- }
-
- private async export_database_inner(opts: TangleDatabaseExportOptions): Promise<void> {
- await this.ensure_ready();
- const app_name = opts.app_name;
- const app_version = opts.app_version;
- const store_key = this.store_key;
- let export_active = false;
-
- try {
- const snapshot_raw = await tangle_db_export_begin();
- export_active = true;
- if (!is_export_snapshot(snapshot_raw)) throw new Error(cl_tangle_error.invalid_response);
- const manifest_rs = snapshot_raw.manifest_rs;
- const db_bytes = snapshot_raw.db_bytes;
- const db_sha256 = await sha256_hex(db_bytes);
- const exported_at = new Date().toISOString();
-
- const manifest_ts_base: TangleDatabaseExportManifestTs = {
- app_name,
- app_version,
- exported_at,
- db_sha256,
- db_size_bytes: db_bytes.byteLength,
- store_key
- };
-
- let manifest: TangleDatabaseExportManifest = {
- rust: manifest_rs,
- client: manifest_ts_base
- };
-
- if (opts.signer) {
- const nostr_event = await opts.signer({ db_sha256, manifest });
- if (nostr_event) {
- manifest = {
- rust: manifest_rs,
- client: {
- ...manifest_ts_base,
- nostr_event
- }
- };
- }
- }
-
- const manifest_json = JSON.stringify(manifest, null, 2);
- const manifest_bytes = new TextEncoder().encode(manifest_json);
- const filename = export_filename(app_name, app_version);
- const mtime = Math.floor(Date.parse(exported_at) / 1000);
- await export_tar_gz(filename, [
- { name: "manifest.json", data: manifest_bytes },
- { name: "tangle.db", data: db_bytes }
- ], mtime);
- } finally {
- if (export_active) tangle_db_export_finish();
- }
- }
-
- async farm_create(opts: IFarmCreate): Promise<IFarmCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_create(this.serialize(opts));
- return this.deserialize<IFarmCreateResolve>(res);
- }
-
- async farm_find_one(opts: IFarmFindOne): Promise<IFarmFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_find_one(this.serialize(opts));
- return this.deserialize<IFarmFindOneResolve>(res);
- }
-
- async farm_find_many(opts?: IFarmFindMany): Promise<IFarmFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IFarmFindManyResolve>(res);
- }
-
- async farm_delete(opts: IFarmDelete): Promise<IFarmDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_delete(this.serialize(opts));
- return this.deserialize<IFarmDeleteResolve>(res);
- }
-
- async farm_update(opts: IFarmUpdate): Promise<IFarmUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_update(this.serialize(opts));
- return this.deserialize<IFarmUpdateResolve>(res);
- }
-
- async plot_create(opts: IPlotCreate): Promise<IPlotCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_create(this.serialize(opts));
- return this.deserialize<IPlotCreateResolve>(res);
- }
-
- async plot_find_one(opts: IPlotFindOne): Promise<IPlotFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_find_one(this.serialize(opts));
- return this.deserialize<IPlotFindOneResolve>(res);
- }
-
- async plot_find_many(opts?: IPlotFindMany): Promise<IPlotFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IPlotFindManyResolve>(res);
- }
-
- async plot_delete(opts: IPlotDelete): Promise<IPlotDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_delete(this.serialize(opts));
- return this.deserialize<IPlotDeleteResolve>(res);
- }
-
- async plot_update(opts: IPlotUpdate): Promise<IPlotUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_update(this.serialize(opts));
- return this.deserialize<IPlotUpdateResolve>(res);
- }
-
- async gcs_location_create(opts: IGcsLocationCreate): Promise<IGcsLocationCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_gcs_location_create(this.serialize(opts));
- return this.deserialize<IGcsLocationCreateResolve>(res);
- }
-
- async gcs_location_find_one(opts: IGcsLocationFindOne): Promise<IGcsLocationFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_gcs_location_find_one(this.serialize(opts));
- return this.deserialize<IGcsLocationFindOneResolve>(res);
- }
-
- async gcs_location_find_many(opts?: IGcsLocationFindMany): Promise<IGcsLocationFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_gcs_location_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IGcsLocationFindManyResolve>(res);
- }
-
- async gcs_location_delete(opts: IGcsLocationDelete): Promise<IGcsLocationDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_gcs_location_delete(this.serialize(opts));
- return this.deserialize<IGcsLocationDeleteResolve>(res);
- }
-
- async gcs_location_update(opts: IGcsLocationUpdate): Promise<IGcsLocationUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_gcs_location_update(this.serialize(opts));
- return this.deserialize<IGcsLocationUpdateResolve>(res);
- }
-
- async farm_gcs_location_create(opts: IFarmGcsLocationCreate): Promise<IFarmGcsLocationCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_gcs_location_create(this.serialize(opts));
- return this.deserialize<IFarmGcsLocationCreateResolve>(res);
- }
-
- async farm_gcs_location_find_one(opts: IFarmGcsLocationFindOne): Promise<IFarmGcsLocationFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_gcs_location_find_one(this.serialize(opts));
- return this.deserialize<IFarmGcsLocationFindOneResolve>(res);
- }
-
- async farm_gcs_location_find_many(opts?: IFarmGcsLocationFindMany): Promise<IFarmGcsLocationFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_gcs_location_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IFarmGcsLocationFindManyResolve>(res);
- }
-
- async farm_gcs_location_delete(opts: IFarmGcsLocationDelete): Promise<IFarmGcsLocationDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_gcs_location_delete(this.serialize(opts));
- return this.deserialize<IFarmGcsLocationDeleteResolve>(res);
- }
-
- async farm_gcs_location_update(opts: IFarmGcsLocationUpdate): Promise<IFarmGcsLocationUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_gcs_location_update(this.serialize(opts));
- return this.deserialize<IFarmGcsLocationUpdateResolve>(res);
- }
-
- async plot_gcs_location_create(opts: IPlotGcsLocationCreate): Promise<IPlotGcsLocationCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_gcs_location_create(this.serialize(opts));
- return this.deserialize<IPlotGcsLocationCreateResolve>(res);
- }
-
- async plot_gcs_location_find_one(opts: IPlotGcsLocationFindOne): Promise<IPlotGcsLocationFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_gcs_location_find_one(this.serialize(opts));
- return this.deserialize<IPlotGcsLocationFindOneResolve>(res);
- }
-
- async plot_gcs_location_find_many(opts?: IPlotGcsLocationFindMany): Promise<IPlotGcsLocationFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_gcs_location_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IPlotGcsLocationFindManyResolve>(res);
- }
-
- async plot_gcs_location_delete(opts: IPlotGcsLocationDelete): Promise<IPlotGcsLocationDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_gcs_location_delete(this.serialize(opts));
- return this.deserialize<IPlotGcsLocationDeleteResolve>(res);
- }
-
- async plot_gcs_location_update(opts: IPlotGcsLocationUpdate): Promise<IPlotGcsLocationUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_gcs_location_update(this.serialize(opts));
- return this.deserialize<IPlotGcsLocationUpdateResolve>(res);
- }
-
- async farm_tag_create(opts: IFarmTagCreate): Promise<IFarmTagCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_tag_create(this.serialize(opts));
- return this.deserialize<IFarmTagCreateResolve>(res);
- }
-
- async farm_tag_find_one(opts: IFarmTagFindOne): Promise<IFarmTagFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_tag_find_one(this.serialize(opts));
- return this.deserialize<IFarmTagFindOneResolve>(res);
- }
-
- async farm_tag_find_many(opts?: IFarmTagFindMany): Promise<IFarmTagFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_tag_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IFarmTagFindManyResolve>(res);
- }
-
- async farm_tag_delete(opts: IFarmTagDelete): Promise<IFarmTagDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_tag_delete(this.serialize(opts));
- return this.deserialize<IFarmTagDeleteResolve>(res);
- }
-
- async farm_tag_update(opts: IFarmTagUpdate): Promise<IFarmTagUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_tag_update(this.serialize(opts));
- return this.deserialize<IFarmTagUpdateResolve>(res);
- }
-
- async plot_tag_create(opts: IPlotTagCreate): Promise<IPlotTagCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_tag_create(this.serialize(opts));
- return this.deserialize<IPlotTagCreateResolve>(res);
- }
-
- async plot_tag_find_one(opts: IPlotTagFindOne): Promise<IPlotTagFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_tag_find_one(this.serialize(opts));
- return this.deserialize<IPlotTagFindOneResolve>(res);
- }
-
- async plot_tag_find_many(opts?: IPlotTagFindMany): Promise<IPlotTagFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_tag_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IPlotTagFindManyResolve>(res);
- }
-
- async plot_tag_delete(opts: IPlotTagDelete): Promise<IPlotTagDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_tag_delete(this.serialize(opts));
- return this.deserialize<IPlotTagDeleteResolve>(res);
- }
-
- async plot_tag_update(opts: IPlotTagUpdate): Promise<IPlotTagUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_plot_tag_update(this.serialize(opts));
- return this.deserialize<IPlotTagUpdateResolve>(res);
- }
-
- async farm_member_create(opts: IFarmMemberCreate): Promise<IFarmMemberCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_create(this.serialize(opts));
- return this.deserialize<IFarmMemberCreateResolve>(res);
- }
-
- async farm_member_find_one(opts: IFarmMemberFindOne): Promise<IFarmMemberFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_find_one(this.serialize(opts));
- return this.deserialize<IFarmMemberFindOneResolve>(res);
- }
-
- async farm_member_find_many(opts?: IFarmMemberFindMany): Promise<IFarmMemberFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IFarmMemberFindManyResolve>(res);
- }
-
- async farm_member_delete(opts: IFarmMemberDelete): Promise<IFarmMemberDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_delete(this.serialize(opts));
- return this.deserialize<IFarmMemberDeleteResolve>(res);
- }
-
- async farm_member_update(opts: IFarmMemberUpdate): Promise<IFarmMemberUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_update(this.serialize(opts));
- return this.deserialize<IFarmMemberUpdateResolve>(res);
- }
-
- async farm_member_claim_create(opts: IFarmMemberClaimCreate): Promise<IFarmMemberClaimCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_claim_create(this.serialize(opts));
- return this.deserialize<IFarmMemberClaimCreateResolve>(res);
- }
-
- async farm_member_claim_find_one(opts: IFarmMemberClaimFindOne): Promise<IFarmMemberClaimFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_claim_find_one(this.serialize(opts));
- return this.deserialize<IFarmMemberClaimFindOneResolve>(res);
- }
-
- async farm_member_claim_find_many(opts?: IFarmMemberClaimFindMany): Promise<IFarmMemberClaimFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_claim_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IFarmMemberClaimFindManyResolve>(res);
- }
-
- async farm_member_claim_delete(opts: IFarmMemberClaimDelete): Promise<IFarmMemberClaimDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_claim_delete(this.serialize(opts));
- return this.deserialize<IFarmMemberClaimDeleteResolve>(res);
- }
-
- async farm_member_claim_update(opts: IFarmMemberClaimUpdate): Promise<IFarmMemberClaimUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_farm_member_claim_update(this.serialize(opts));
- return this.deserialize<IFarmMemberClaimUpdateResolve>(res);
- }
-
- async log_error_create(opts: ILogErrorCreate): Promise<ILogErrorCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_log_error_create(this.serialize(opts));
- return this.deserialize<ILogErrorCreateResolve>(res);
- }
-
- async log_error_find_one(opts: ILogErrorFindOne): Promise<ILogErrorFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_log_error_find_one(this.serialize(opts));
- return this.deserialize<ILogErrorFindOneResolve>(res);
- }
-
- async log_error_find_many(opts?: ILogErrorFindMany): Promise<ILogErrorFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_log_error_find_many(this.serialize(opts ?? {}));
- return this.deserialize<ILogErrorFindManyResolve>(res);
- }
-
- async log_error_delete(opts: ILogErrorDelete): Promise<ILogErrorDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_log_error_delete(this.serialize(opts));
- return this.deserialize<ILogErrorDeleteResolve>(res);
- }
-
- async log_error_update(opts: ILogErrorUpdate): Promise<ILogErrorUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_log_error_update(this.serialize(opts));
- return this.deserialize<ILogErrorUpdateResolve>(res);
- }
-
- async media_image_create(opts: IMediaImageCreate): Promise<IMediaImageCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_media_image_create(this.serialize(opts));
- return this.deserialize<IMediaImageCreateResolve>(res);
- }
-
- async media_image_find_one(opts: IMediaImageFindOne): Promise<IMediaImageFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_media_image_find_one(this.serialize(opts));
- return this.deserialize<IMediaImageFindOneResolve>(res);
- }
-
- async media_image_find_many(opts?: IMediaImageFindMany): Promise<IMediaImageFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_media_image_find_many(this.serialize(opts ?? {}));
- return this.deserialize<IMediaImageFindManyResolve>(res);
- }
-
- async media_image_delete(opts: IMediaImageDelete): Promise<IMediaImageDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_media_image_delete(this.serialize(opts));
- return this.deserialize<IMediaImageDeleteResolve>(res);
- }
-
- async media_image_update(opts: IMediaImageUpdate): Promise<IMediaImageUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_media_image_update(this.serialize(opts));
- return this.deserialize<IMediaImageUpdateResolve>(res);
- }
-
- async nostr_profile_create(opts: INostrProfileCreate): Promise<INostrProfileCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_create(this.serialize(opts));
- return this.deserialize<INostrProfileCreateResolve>(res);
- }
-
- async nostr_profile_find_one(opts: INostrProfileFindOne): Promise<INostrProfileFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_find_one(this.serialize(opts));
- return this.deserialize<INostrProfileFindOneResolve>(res);
- }
-
- async nostr_profile_find_many(opts?: INostrProfileFindMany): Promise<INostrProfileFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_find_many(this.serialize(opts ?? {}));
- return this.deserialize<INostrProfileFindManyResolve>(res);
- }
-
- async nostr_profile_delete(opts: INostrProfileDelete): Promise<INostrProfileDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_delete(this.serialize(opts));
- return this.deserialize<INostrProfileDeleteResolve>(res);
- }
-
- async nostr_profile_update(opts: INostrProfileUpdate): Promise<INostrProfileUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_update(this.serialize(opts));
- return this.deserialize<INostrProfileUpdateResolve>(res);
- }
-
- async nostr_event_state_create(opts: INostrEventStateCreate): Promise<INostrEventStateCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_event_state_create(this.serialize(opts));
- return this.deserialize<INostrEventStateCreateResolve>(res);
- }
-
- async nostr_event_state_find_one(opts: INostrEventStateFindOne): Promise<INostrEventStateFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_event_state_find_one(this.serialize(opts));
- return this.deserialize<INostrEventStateFindOneResolve>(res);
- }
-
- async nostr_event_state_find_many(opts?: INostrEventStateFindMany): Promise<INostrEventStateFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_event_state_find_many(this.serialize(opts ?? {}));
- return this.deserialize<INostrEventStateFindManyResolve>(res);
- }
-
- async nostr_event_state_delete(opts: INostrEventStateDelete): Promise<INostrEventStateDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_event_state_delete(this.serialize(opts));
- return this.deserialize<INostrEventStateDeleteResolve>(res);
- }
-
- async nostr_event_state_update(opts: INostrEventStateUpdate): Promise<INostrEventStateUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_event_state_update(this.serialize(opts));
- return this.deserialize<INostrEventStateUpdateResolve>(res);
- }
-
- async nostr_relay_create(opts: INostrRelayCreate): Promise<INostrRelayCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_relay_create(this.serialize(opts));
- return this.deserialize<INostrRelayCreateResolve>(res);
- }
-
- async nostr_relay_find_one(opts: INostrRelayFindOne): Promise<INostrRelayFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_relay_find_one(this.serialize(opts));
- return this.deserialize<INostrRelayFindOneResolve>(res);
- }
-
- async nostr_relay_find_many(opts?: INostrRelayFindMany): Promise<INostrRelayFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_relay_find_many(this.serialize(opts ?? {}));
- return this.deserialize<INostrRelayFindManyResolve>(res);
- }
-
- async nostr_relay_delete(opts: INostrRelayDelete): Promise<INostrRelayDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_relay_delete(this.serialize(opts));
- return this.deserialize<INostrRelayDeleteResolve>(res);
- }
-
- async nostr_relay_update(opts: INostrRelayUpdate): Promise<INostrRelayUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_relay_update(this.serialize(opts));
- return this.deserialize<INostrRelayUpdateResolve>(res);
- }
-
- async trade_product_create(opts: ITradeProductCreate): Promise<ITradeProductCreateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_create(this.serialize(opts));
- return this.deserialize<ITradeProductCreateResolve>(res);
- }
-
- async trade_product_find_one(opts: ITradeProductFindOne): Promise<ITradeProductFindOneResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_find_one(this.serialize(opts));
- return this.deserialize<ITradeProductFindOneResolve>(res);
- }
-
- async trade_product_find_many(opts?: ITradeProductFindMany): Promise<ITradeProductFindManyResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_find_many(this.serialize(opts ?? {}));
- return this.deserialize<ITradeProductFindManyResolve>(res);
- }
-
- async trade_product_delete(opts: ITradeProductDelete): Promise<ITradeProductDeleteResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_delete(this.serialize(opts));
- return this.deserialize<ITradeProductDeleteResolve>(res);
- }
-
- async trade_product_update(opts: ITradeProductUpdate): Promise<ITradeProductUpdateResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_update(this.serialize(opts));
- return this.deserialize<ITradeProductUpdateResolve>(res);
- }
-
- async nostr_profile_relay_set(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_relay_set(this.serialize(opts));
- return this.deserialize<INostrProfileRelayResolve>(res);
- }
-
- async nostr_profile_relay_unset(opts: INostrProfileRelayRelation): Promise<INostrProfileRelayResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_nostr_profile_relay_unset(this.serialize(opts));
- return this.deserialize<INostrProfileRelayResolve>(res);
- }
-
- async trade_product_location_set(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_location_set(this.serialize(opts));
- return this.deserialize<ITradeProductLocationResolve>(res);
- }
-
- async trade_product_location_unset(opts: ITradeProductLocationRelation): Promise<ITradeProductLocationResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_location_unset(this.serialize(opts));
- return this.deserialize<ITradeProductLocationResolve>(res);
- }
-
- async trade_product_media_set(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_media_set(this.serialize(opts));
- return this.deserialize<ITradeProductMediaResolve>(res);
- }
-
- async trade_product_media_unset(opts: ITradeProductMediaRelation): Promise<ITradeProductMediaResolve | IError<string>> {
- await this.ensure_ready();
- const res = await tangle_db_trade_product_media_unset(this.serialize(opts));
- return this.deserialize<ITradeProductMediaResolve>(res);
- }
-
-}
-
-export const web_tangle_database_create = async (config?: WebTangleDatabaseConfig): Promise<WebTangleDatabase> => {
- const db = new WebTangleDatabase(config);
- await db.init();
- return db;
-};
diff --git a/geo/package.json b/geo/package.json
@@ -35,7 +35,7 @@
},
"dependencies": {
"@radroots/utils": "workspace:*",
- "@radroots/tangle-db-schema-bindings": "workspace:*",
+ "@radroots/replica-db-schema-bindings": "workspace:*",
"geohashing": "^2.0.1",
"zod": "^4.0.5"
}
diff --git a/geo/src/gcs.ts b/geo/src/gcs.ts
@@ -1,4 +1,4 @@
-import type { GcsLocation } from "@radroots/tangle-db-schema-bindings";
+import type { GcsLocation } from "@radroots/replica-db-schema-bindings";
import type { LocationBasis } from "./types.js";
export const gcs_to_location_basis = ({
diff --git a/locales/src/messages/en/error.json b/locales/src/messages/en/error.json
@@ -72,8 +72,8 @@
"missing_version_metadata": "Import file is missing version metadata.",
"no_file_chosen": "Choose a backup file before continuing.",
"storage_mismatch": "Import failed: {{label}} storage mismatch (app={{app_database}}/{{app_store}}, backup={{backup_database}}/{{backup_store}}).",
- "tangle_db_version_mismatch": "Import file tangle-db version does not match metadata.",
- "tangle_store_key_mismatch": "Import failed: tangle DB store key mismatch (app={{app_store_key}}, backup={{backup_store_key}}).",
+ "replica_db_version_mismatch": "Import file replica-db version does not match metadata.",
+ "replica_store_key_mismatch": "Import failed: replica DB store key mismatch (app={{app_store_key}}, backup={{backup_store_key}}).",
"unsupported_backup_version": "Unsupported backup version ({{backup_version}}); expected {{expected_version}}."
},
"profile": {
diff --git a/nostr/package.json b/nostr/package.json
@@ -40,9 +40,9 @@
"@radroots/events-codec-wasm": "workspace:*",
"@radroots/trade-bindings": "workspace:*",
"@radroots/utils": "workspace:*",
- "@welshman/net": "workspace:*",
- "@welshman/signer": "workspace:*",
- "@welshman/util": "workspace:*",
+ "@welshman/net": "0.8.4",
+ "@welshman/signer": "0.8.4",
+ "@welshman/util": "0.8.4",
"nostr-geotags": "^0.7.2",
"nostr-tools": "^2.10.4",
"zod": "^4.2.1"
diff --git a/nostr/src/domain/trade/lib.ts b/nostr/src/domain/trade/lib.ts
@@ -1,44 +0,0 @@
-import {
- KIND_TRADE_LISTING_ACCEPT_REQ,
- KIND_TRADE_LISTING_ACCEPT_RES,
- KIND_TRADE_LISTING_CANCEL_REQ,
- KIND_TRADE_LISTING_CANCEL_RES,
- KIND_TRADE_LISTING_CONVEYANCE_REQ,
- KIND_TRADE_LISTING_CONVEYANCE_RES,
- KIND_TRADE_LISTING_FULFILL_REQ,
- KIND_TRADE_LISTING_FULFILL_RES,
- KIND_TRADE_LISTING_INVOICE_REQ,
- KIND_TRADE_LISTING_INVOICE_RES,
- KIND_TRADE_LISTING_ORDER_REQ,
- KIND_TRADE_LISTING_ORDER_RES,
- KIND_TRADE_LISTING_PAYMENT_REQ,
- KIND_TRADE_LISTING_PAYMENT_RES,
- KIND_TRADE_LISTING_RECEIPT_REQ,
- KIND_TRADE_LISTING_RECEIPT_RES,
- KIND_TRADE_LISTING_REFUND_REQ,
- KIND_TRADE_LISTING_REFUND_RES,
-} from "@radroots/trade-bindings";
-
-export const REQUEST_KINDS: Record<string, number> = {
- order: KIND_TRADE_LISTING_ORDER_REQ,
- accept: KIND_TRADE_LISTING_ACCEPT_REQ,
- conveyance: KIND_TRADE_LISTING_CONVEYANCE_REQ,
- invoice: KIND_TRADE_LISTING_INVOICE_REQ,
- payment: KIND_TRADE_LISTING_PAYMENT_REQ,
- fulfillment: KIND_TRADE_LISTING_FULFILL_REQ,
- receipt: KIND_TRADE_LISTING_RECEIPT_REQ,
- cancel: KIND_TRADE_LISTING_CANCEL_REQ,
- refund: KIND_TRADE_LISTING_REFUND_REQ,
-};
-
-export const RESULT_KINDS: Record<string, number> = {
- order: KIND_TRADE_LISTING_ORDER_RES,
- accept: KIND_TRADE_LISTING_ACCEPT_RES,
- conveyance: KIND_TRADE_LISTING_CONVEYANCE_RES,
- invoice: KIND_TRADE_LISTING_INVOICE_RES,
- payment: KIND_TRADE_LISTING_PAYMENT_RES,
- fulfillment: KIND_TRADE_LISTING_FULFILL_RES,
- receipt: KIND_TRADE_LISTING_RECEIPT_RES,
- cancel: KIND_TRADE_LISTING_CANCEL_RES,
- refund: KIND_TRADE_LISTING_REFUND_RES,
-};
diff --git a/nostr/src/domain/trade/listing/accept/lib.ts b/nostr/src/domain/trade/listing/accept/lib.ts
@@ -1,63 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_ACCEPT_REQ,
- KIND_TRADE_LISTING_ACCEPT_RES,
- MARKER_LISTING,
- MARKER_ORDER_RESULT,
- TradeListingAcceptRequest,
- TradeListingAcceptResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_accept_request = async (
- opts: NostrEventFigure<{ data: TradeListingAcceptRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.order_result_event_id, MARKER_ORDER_RESULT),
- make_event_input(data.listing_event_id, MARKER_LISTING),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_ACCEPT_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_ACCEPT_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_accept_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingAcceptResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_ACCEPT_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_ACCEPT_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/conveyance/lib.ts b/nostr/src/domain/trade/listing/conveyance/lib.ts
@@ -1,64 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_CONVEYANCE_REQ,
- KIND_TRADE_LISTING_CONVEYANCE_RES,
- MARKER_ACCEPT_RESULT,
- MARKER_PAYLOAD,
- TradeListingConveyanceRequest,
- TradeListingConveyanceResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
- make_text_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_conveyance_request = async (
- opts: NostrEventFigure<{ data: TradeListingConveyanceRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.accept_result_event_id, MARKER_ACCEPT_RESULT),
- make_text_input({ method: data.method }, MARKER_PAYLOAD),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_CONVEYANCE_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_CONVEYANCE_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_conveyance_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingConveyanceResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_CONVEYANCE_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_CONVEYANCE_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/fulfillment/lib.ts b/nostr/src/domain/trade/listing/fulfillment/lib.ts
@@ -1,61 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_FULFILL_REQ,
- KIND_TRADE_LISTING_FULFILL_RES,
- MARKER_PAYMENT_RESULT,
- TradeListingFulfillmentRequest,
- TradeListingFulfillmentResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_fulfillment_request = async (
- opts: NostrEventFigure<{ data: TradeListingFulfillmentRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.payment_result_event_id, MARKER_PAYMENT_RESULT),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_FULFILL_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_FULFILL_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_fulfillment_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingFulfillmentResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_FULFILL_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_FULFILL_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/invoice/lib.ts b/nostr/src/domain/trade/listing/invoice/lib.ts
@@ -1,71 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_INVOICE_REQ,
- KIND_TRADE_LISTING_INVOICE_RES,
- MARKER_ACCEPT_RESULT,
- TradeListingInvoiceRequest,
- TradeListingInvoiceResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_invoice_request = async (
- opts: NostrEventFigure<{ data: TradeListingInvoiceRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.accept_result_event_id, MARKER_ACCEPT_RESULT),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_INVOICE_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_INVOICE_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_invoice_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingInvoiceResult | string;
- options?: Omit<CommonResultOpts, "payment_sat" | "payment_bolt11"> & {
- chain?: { e_root: string; d?: string; e_prev?: string };
- };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const parsed = typeof content === "string" ? undefined : content;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_INVOICE_RES,
- request_event_id,
- options,
- parsed
- ? {
- payment_sat: parsed.total_sat,
- payment_bolt11: parsed.bolt11 ?? undefined,
- }
- : undefined,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_INVOICE_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/order/lib.ts b/nostr/src/domain/trade/listing/order/lib.ts
@@ -1,64 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_ORDER_REQ,
- KIND_TRADE_LISTING_ORDER_RES,
- MARKER_LISTING,
- MARKER_PAYLOAD,
- TradeListingOrderRequest,
- TradeListingOrderResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
- make_text_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_order_request = async (
- opts: NostrEventFigure<{ data: TradeListingOrderRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.event.id, MARKER_LISTING, data.event.relays ?? undefined),
- make_text_input(data.payload, MARKER_PAYLOAD),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_ORDER_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_ORDER_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_order_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingOrderResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_ORDER_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_ORDER_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/payment/lib.ts b/nostr/src/domain/trade/listing/payment/lib.ts
@@ -1,64 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_PAYMENT_REQ,
- KIND_TRADE_LISTING_PAYMENT_RES,
- MARKER_INVOICE_RESULT,
- MARKER_PROOF,
- TradeListingPaymentProofRequest,
- TradeListingPaymentResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
- make_text_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_payment_request = async (
- opts: NostrEventFigure<{ data: TradeListingPaymentProofRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.invoice_result_event_id, MARKER_INVOICE_RESULT),
- make_text_input(data.proof, MARKER_PROOF),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_PAYMENT_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_PAYMENT_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_payment_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingPaymentResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_PAYMENT_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_PAYMENT_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/receipt/lib.ts b/nostr/src/domain/trade/listing/receipt/lib.ts
@@ -1,64 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import {
- KIND_TRADE_LISTING_RECEIPT_REQ,
- KIND_TRADE_LISTING_RECEIPT_RES,
- MARKER_FULFILLMENT_RESULT,
- MARKER_PAYLOAD,
- TradeListingReceiptRequest,
- TradeListingReceiptResult,
-} from "@radroots/trade-bindings";
-import { nostr_event_create } from "../../../../events/lib.js";
-import type { NostrEventFigure, NostrSignedEvent } from "../../../../types/nostr.js";
-import {
- build_request_tags,
- build_result_tags,
- CommonRequestOpts,
- CommonResultOpts,
- make_event_input,
- make_text_input,
-} from "../../tags.js";
-import { tags_trade_listing_chain } from "../tags.js";
-
-export const nostr_event_trade_listing_receipt_request = async (
- opts: NostrEventFigure<{ data: TradeListingReceiptRequest; options?: CommonRequestOpts }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { data, options } = opts;
-
- const inputs: RadrootsJobInput[] = [
- make_event_input(data.fulfillment_result_event_id, MARKER_FULFILLMENT_RESULT),
- ...(data.note ? [make_text_input({ note: data.note }, MARKER_PAYLOAD)] : []),
- ];
-
- const tags = await build_request_tags(KIND_TRADE_LISTING_RECEIPT_REQ, inputs, options);
-
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_RECEIPT_REQ, content: "", tags },
- });
-};
-
-export const nostr_event_trade_listing_receipt_result = async (
- opts: NostrEventFigure<{
- request_event_id: string;
- content: TradeListingReceiptResult | string;
- options?: CommonResultOpts & { chain?: { e_root: string; d?: string; e_prev?: string } };
- }>,
-): Promise<NostrSignedEvent | undefined> => {
- const { request_event_id, content, options } = opts;
-
- const base_tags = await build_result_tags(
- KIND_TRADE_LISTING_RECEIPT_RES,
- request_event_id,
- options,
- );
-
- const tags = options?.chain
- ? [...base_tags, ...tags_trade_listing_chain(options.chain)]
- : base_tags;
-
- const content_body = typeof content === "string" ? content : JSON.stringify(content);
- return nostr_event_create({
- ...opts,
- basis: { kind: KIND_TRADE_LISTING_RECEIPT_RES, content: content_body, tags },
- });
-};
diff --git a/nostr/src/domain/trade/listing/tags.ts b/nostr/src/domain/trade/listing/tags.ts
@@ -1,14 +0,0 @@
-import { TAG_D, TAG_E_PREV, TAG_E_ROOT } from "@radroots/events-bindings";
-import { NostrEventTags } from "../../../types/lib.js";
-
-export const tags_trade_listing_chain = (opts: {
- e_root: string;
- d?: string;
- e_prev?: string;
-}): NostrEventTags => {
- const tags: NostrEventTags = [];
- tags.push([TAG_E_ROOT, opts.e_root]);
- if (opts.e_prev) tags.push([TAG_E_PREV, opts.e_prev]);
- if (opts.d) tags.push([TAG_D, opts.d]);
- return tags;
-};
diff --git a/nostr/src/domain/trade/tags.ts b/nostr/src/domain/trade/tags.ts
@@ -1,88 +0,0 @@
-import { RadrootsJobInput } from "@radroots/events-bindings";
-import { tags_job_request, tags_job_result } from "../../events/job/tags.js";
-
-export type CommonRequestOpts = {
- output?: string;
- bid_sat?: number;
- relays?: string[];
- providers?: string[];
- topics?: string[];
- encrypted?: boolean;
- params?: Array<{ key: string; value: string }>;
-};
-
-export type CommonResultOpts = {
- request_relay_hint?: string;
- request_json?: string;
- customer_pubkey?: string;
- payment_sat?: number;
- payment_bolt11?: string;
- encrypted?: boolean;
- include_inputs?: string[];
- chain?: { e_root: string; d?: string; e_prev?: string };
-};
-
-export const make_event_input = (
- id: string,
- marker: string,
- relay?: string,
-): RadrootsJobInput => ({
- data: id,
- input_type: "event",
- ...(relay ? { relay } : {}),
- marker,
-});
-
-export const make_text_input = (
- payload: unknown,
- marker: string,
-): RadrootsJobInput => ({
- data: typeof payload === "string" ? payload : JSON.stringify(payload),
- input_type: "text",
- marker,
-});
-
-export const build_request_tags = async (
- kind: number,
- inputs: RadrootsJobInput[],
- opts?: CommonRequestOpts,
-) =>
- await tags_job_request({
- kind,
- inputs,
- output: opts?.output,
- params: opts?.params ?? [],
- bid_sat: opts?.bid_sat,
- relays: opts?.relays ?? [],
- providers: opts?.providers ?? [],
- topics: opts?.topics ?? [],
- encrypted: !!opts?.encrypted,
- });
-
-export const build_result_tags = async (
- kind: number,
- request_event_id: string,
- opts?: CommonResultOpts,
- extra?: {
- inputs?: RadrootsJobInput[];
- payment_sat?: number;
- payment_bolt11?: string;
- },
-) =>
- await tags_job_result({
- kind,
- request_event: {
- id: request_event_id,
- ...(opts?.request_relay_hint ? { relays: opts.request_relay_hint } : {}),
- },
- request_json: opts?.request_json,
- inputs: !opts?.encrypted && extra?.inputs?.length ? extra.inputs : [],
- customer_pubkey: opts?.customer_pubkey,
- payment:
- extra?.payment_sat !== undefined
- ? { amount_sat: extra.payment_sat, bolt11: extra.payment_bolt11 }
- : opts?.payment_sat !== undefined
- ? { amount_sat: opts.payment_sat, bolt11: opts.payment_bolt11 }
- : undefined,
- encrypted: !!opts?.encrypted,
- });
diff --git a/nostr/src/events/comment/parse.ts b/nostr/src/events/comment/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_COMMENT, radroots_comment_schema, type RadrootsComment } from "@radroots/events-bindings";
+import { KIND_COMMENT, type RadrootsComment } 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";
@@ -12,7 +12,7 @@ export const parse_nostr_comment_event = (
if (!ev) return undefined;
try {
const parsed = JSON.parse(event.content);
- const comment = radroots_comment_schema.parse(parsed);
+ const comment = parsed as RadrootsComment;
return { ...ev, comment };
} catch {
return undefined;
diff --git a/nostr/src/events/farm/parse.ts b/nostr/src/events/farm/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_FARM, radroots_farm_schema, type RadrootsFarm } from "@radroots/events-bindings";
+import { KIND_FARM, type RadrootsFarm } from "@radroots/events-bindings";
import type { NostrEvent } from "../../types/nostr.js";
import { get_event_tag, parse_nostr_event_basis } from "../lib.js";
import type { NostrEventBasis } from "../subscription.js";
@@ -14,7 +14,7 @@ export const parse_nostr_farm_event = (
if (!d_tag) return undefined;
try {
const parsed = JSON.parse(event.content);
- const farm = radroots_farm_schema.parse(parsed);
+ const farm = parsed as RadrootsFarm;
if (farm.d_tag !== d_tag) return undefined;
return { ...ev, farm };
} catch {
diff --git a/nostr/src/events/follow/parse.ts b/nostr/src/events/follow/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_FOLLOW, radroots_follow_schema, type RadrootsFollow } from "@radroots/events-bindings";
+import { KIND_FOLLOW, type RadrootsFollow } 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";
@@ -12,7 +12,7 @@ export const parse_nostr_follow_event = (
if (!ev) return undefined;
try {
const parsed = JSON.parse(event.content);
- const follow = radroots_follow_schema.parse(parsed);
+ const follow = parsed as RadrootsFollow;
return { ...ev, follow };
} catch {
return undefined;
diff --git a/nostr/src/events/job/utils.ts b/nostr/src/events/job/utils.ts
@@ -1,20 +1,6 @@
-import { JobInputType, KIND_JOB_FEEDBACK } from "@radroots/events-bindings";
-import { TradeListingStage } from "@radroots/trade-bindings";
-import { REQUEST_KINDS, RESULT_KINDS } from "../../domain/trade/lib.js";
+import type { JobInputType } from "@radroots/events-bindings";
import type { NostrEventTags } from "../../types/lib.js";
-const TRADE_LISTING_STAGE_KEYS: TradeListingStage["kind"][] = [
- "order",
- "accept",
- "conveyance",
- "invoice",
- "payment",
- "fulfillment",
- "receipt",
- "cancel",
- "refund",
-];
-
export function get_job_input_data_for_marker(
tags: NostrEventTags,
marker: string,
@@ -28,16 +14,3 @@ export function get_job_input_data_for_marker(
}
return undefined;
}
-
-export function get_trade_listing_stage_from_event_kind(
- kind: number,
-): TradeListingStage | undefined {
- for (const key of TRADE_LISTING_STAGE_KEYS) {
- if (REQUEST_KINDS[key] === kind) return { kind: key };
- }
- for (const key of TRADE_LISTING_STAGE_KEYS) {
- if (RESULT_KINDS[key] === kind) return { kind: key };
- }
- if (kind === KIND_JOB_FEEDBACK) return { kind: "order" };
- return undefined;
-}
diff --git a/nostr/src/events/list/parse.ts b/nostr/src/events/list/parse.ts
@@ -1,5 +1,4 @@
import type { RadrootsList } from "@radroots/events-bindings";
-import { radroots_list_schema } from "@radroots/events-bindings";
import type { NostrEventTags } from "../../types/lib.js";
import type { NostrEvent } from "../../types/nostr.js";
import type { NostrEventBasis } from "../subscription.js";
@@ -23,7 +22,7 @@ export const parse_nostr_list_event = (
content: event.content ?? "",
entries: list_entries_from_tags(event.tags),
};
- const list = radroots_list_schema.parse(list_raw);
+ const list = list_raw as RadrootsList;
return {
id: event.id,
published_at: event.created_at,
diff --git a/nostr/src/events/list_set/parse.ts b/nostr/src/events/list_set/parse.ts
@@ -1,5 +1,4 @@
import type { RadrootsListSet } from "@radroots/events-bindings";
-import { radroots_list_set_schema } from "@radroots/events-bindings";
import type { NostrEventTags } from "../../types/lib.js";
import type { NostrEvent } from "../../types/nostr.js";
import type { NostrEventBasis } from "../subscription.js";
@@ -33,7 +32,7 @@ export const parse_nostr_list_set_event = (
description: get_event_tag(event.tags, "description") || undefined,
image: get_event_tag(event.tags, "image") || undefined,
};
- const list_set = radroots_list_set_schema.parse(list_set_raw);
+ const list_set = list_set_raw as RadrootsListSet;
return {
id: event.id,
published_at: event.created_at,
diff --git a/nostr/src/events/listing/parse.ts b/nostr/src/events/listing/parse.ts
@@ -1,23 +1,18 @@
-import { RadrootsCoreUnit } from "@radroots/core-bindings";
import type {
RadrootsCoreDiscount,
+ RadrootsCoreDecimal,
RadrootsCoreMoney,
RadrootsCoreQuantity,
RadrootsCoreQuantityPrice,
} from "@radroots/core-bindings";
import {
KIND_LISTING,
- radroots_listing_bin_schema,
- radroots_listing_discount_schema,
- radroots_listing_image_schema,
- radroots_listing_location_schema,
- radroots_listing_product_schema,
- radroots_listing_schema,
type RadrootsListing,
type RadrootsListingAvailability,
type RadrootsListingDeliveryMethod,
- type RadrootsListingFarmRef,
+ type RadrootsFarmRef,
type RadrootsListingImage,
+ type RadrootsListingProduct,
type RadrootsListingStatus,
} from "@radroots/events-bindings";
import type { NostrEvent } from "../../types/nostr.js";
@@ -33,7 +28,7 @@ type ListingBinDraft = {
bin_id: string;
quantity?: RadrootsCoreQuantity;
price_per_canonical_unit?: RadrootsCoreQuantityPrice;
- display_amount?: number;
+ display_amount?: RadrootsCoreDecimal;
display_unit?: CoreUnit;
display_label?: string;
display_price?: RadrootsCoreMoney;
@@ -74,8 +69,13 @@ const clean_string = (value?: string | null) => value?.trim() || undefined;
const parse_currency_code = (code?: string): CoreCurrency | undefined => {
const cleaned = clean_string(code);
if (!cleaned || cleaned.length < 3) return undefined;
- const upper = cleaned.toUpperCase();
- return [upper.charCodeAt(0), upper.charCodeAt(1), upper.charCodeAt(2)] as CoreCurrency;
+ return cleaned.toUpperCase();
+};
+
+const parse_decimal = (value?: string): RadrootsCoreDecimal | undefined => {
+ const cleaned = clean_string(value);
+ if (!cleaned) return undefined;
+ return Number.isFinite(Number(cleaned)) ? cleaned : undefined;
};
const parse_unit_code = (unit?: string): CoreUnit | undefined => {
@@ -83,35 +83,35 @@ const parse_unit_code = (unit?: string): CoreUnit | undefined => {
case "each":
case "ea":
case "count":
- return RadrootsCoreUnit.Each;
+ return "each";
case "kg":
case "kilogram":
case "kilograms":
- return RadrootsCoreUnit.MassKg;
+ return "kg";
case "g":
case "gram":
case "grams":
- return RadrootsCoreUnit.MassG;
+ return "g";
case "oz":
case "ounce":
case "ounces":
- return RadrootsCoreUnit.MassOz;
+ return "oz";
case "lb":
case "pound":
case "pounds":
- return RadrootsCoreUnit.MassLb;
+ return "lb";
case "l":
case "liter":
case "litre":
case "liters":
case "litres":
- return RadrootsCoreUnit.VolumeL;
+ return "l";
case "ml":
case "milliliter":
case "millilitre":
case "milliliters":
case "millilitres":
- return RadrootsCoreUnit.VolumeMl;
+ return "ml";
default:
return undefined;
}
@@ -121,12 +121,12 @@ const parse_bin_tag = (tag: string[]): ListingBinDraft | undefined => {
if (tag.length < 4) return undefined;
const bin_id = clean_string(tag[1]);
if (!bin_id) return undefined;
- const amount = to_number(tag[2]);
+ const amount = parse_decimal(tag[2]);
const unit = parse_unit_code(tag[3]);
if (amount === undefined || !unit) return undefined;
- const quantity: RadrootsCoreQuantity = { amount, unit };
+ const quantity: RadrootsCoreQuantity = { amount, unit, label: null };
const draft: ListingBinDraft = { bin_id, quantity };
- const display_amount = to_number(tag[4]);
+ const display_amount = parse_decimal(tag[4]);
const display_unit = parse_unit_code(tag[5]);
if (display_amount !== undefined && display_unit) {
draft.display_amount = display_amount;
@@ -141,16 +141,16 @@ const parse_bin_price_tag = (tag: string[]): ListingBinDraft | undefined => {
if (tag.length < 6) return undefined;
const bin_id = clean_string(tag[1]);
if (!bin_id) return undefined;
- const amount = to_number(tag[2]);
+ const amount = parse_decimal(tag[2]);
const currency = parse_currency_code(tag[3]);
- const quantity_amount = to_number(tag[4]);
+ const quantity_amount = parse_decimal(tag[4]);
const quantity_unit = parse_unit_code(tag[5]);
if (amount === undefined || !currency || quantity_amount === undefined || !quantity_unit) return undefined;
const money: RadrootsCoreMoney = { amount, currency };
- const quantity: RadrootsCoreQuantity = { amount: quantity_amount, unit: quantity_unit };
+ const quantity: RadrootsCoreQuantity = { amount: quantity_amount, unit: quantity_unit, label: null };
const price_per_canonical_unit: RadrootsCoreQuantityPrice = { amount: money, quantity };
const draft: ListingBinDraft = { bin_id, price_per_canonical_unit };
- const display_price_amount = to_number(tag[6]);
+ const display_price_amount = parse_decimal(tag[6]);
const display_price_unit = parse_unit_code(tag[7]);
if (display_price_amount !== undefined && display_price_unit) {
draft.display_price = { amount: display_price_amount, currency };
@@ -163,7 +163,7 @@ const parse_discount_tag = (tag: string[]): RadrootsCoreDiscount | undefined =>
if (tag[0] !== "radroots:discount" || !tag[1]) return undefined;
try {
const payload = JSON.parse(tag[1]);
- return radroots_listing_discount_schema.parse(payload);
+ return payload as RadrootsCoreDiscount;
} catch {
return undefined;
}
@@ -176,10 +176,10 @@ const parse_image_tag = (tag: string[]): RadrootsListingImage | undefined => {
const [w, h] = tag[2].split("x").map(v => Number(v));
if (Number.isFinite(w) && Number.isFinite(h)) image.size = { w, h };
}
- return radroots_listing_image_schema.parse(image);
+ return image;
};
-const parse_farm_ref = (farm_pubkey?: string, farm_address?: string): RadrootsListingFarmRef | undefined => {
+const parse_farm_ref = (farm_pubkey?: string, farm_address?: string): RadrootsFarmRef | undefined => {
const pubkey = clean_string(farm_pubkey);
const address = clean_string(farm_address);
if (!pubkey || !address) return undefined;
@@ -251,7 +251,8 @@ export const parse_nostr_listing_event = (
const tags = event.tags;
const d_tag = get_event_tag(tags, "d");
- if (!clean_string(d_tag)) return undefined;
+ const listing_d_tag = clean_string(d_tag);
+ if (!listing_d_tag) return undefined;
const farm_pubkey = get_event_tag(tags, "p");
const farm_address = get_event_tag(tags, "a");
const farm = parse_farm_ref(farm_pubkey, farm_address);
@@ -269,7 +270,21 @@ export const parse_nostr_listing_event = (
year: get_event_tag(tags, "year"),
};
- const product = radroots_listing_product_schema.parse(product_raw);
+ const product_key = clean_string(product_raw.key);
+ const product_title = clean_string(product_raw.title);
+ const product_category = clean_string(product_raw.category);
+ if (!product_key || !product_title || !product_category) return undefined;
+ const product: RadrootsListingProduct = {
+ key: product_key,
+ title: product_title,
+ category: product_category,
+ summary: clean_string(product_raw.summary) ?? null,
+ process: clean_string(product_raw.process) ?? null,
+ lot: clean_string(product_raw.lot) ?? null,
+ location: clean_string(product_raw.location) ?? null,
+ profile: clean_string(product_raw.profile) ?? null,
+ year: clean_string(product_raw.year) ?? null,
+ };
const bin_drafts: Record<string, ListingBinDraft> = {};
const bin_order: string[] = [];
@@ -283,8 +298,7 @@ export const parse_nostr_listing_event = (
}
const bins = bin_order
.map(bin_id => bin_drafts[bin_id])
- .filter(is_listing_bin)
- .map(bin => radroots_listing_bin_schema.parse(bin));
+ .filter(is_listing_bin);
if (!bins.length) return undefined;
const primary_bin_tag = clean_string(get_event_tag(tags, "radroots:primary_bin"));
const primary_bin_id = bins.some(bin => bin.bin_id === primary_bin_tag)
@@ -322,26 +336,36 @@ export const parse_nostr_listing_event = (
}
const location = location_raw.primary
- ? radroots_listing_location_schema.parse(location_raw)
+ ? {
+ primary: location_raw.primary,
+ city: location_raw.city ?? null,
+ region: location_raw.region ?? null,
+ country: location_raw.country ?? null,
+ lat: location_raw.lat ?? null,
+ lng: location_raw.lng ?? null,
+ geohash: location_raw.geohash ?? null,
+ }
: undefined;
- const inventory_available = to_number(get_event_tag(tags, "inventory"));
+ const inventory_available = parse_decimal(get_event_tag(tags, "inventory"));
const availability = parse_listing_availability(tags);
const delivery_method = parse_delivery_method(tags);
- const listing_base = radroots_listing_schema.parse({
- d_tag,
+ const listing: RadrootsListing = {
+ d_tag: listing_d_tag,
+ farm,
product,
primary_bin_id,
bins,
- discounts: discounts.length ? discounts : undefined,
+ discounts: discounts.length ? discounts : null,
inventory_available,
availability,
delivery_method,
location,
- images: images.length ? images : undefined,
- });
- const listing: RadrootsListing = { ...listing_base, farm };
+ images: images.length ? images : null,
+ resource_area: null,
+ plot: null,
+ };
return { ...ev, listing };
} catch {
return undefined;
diff --git a/nostr/src/events/plot/parse.ts b/nostr/src/events/plot/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_FARM, KIND_PLOT, radroots_plot_schema, type RadrootsPlot } from "@radroots/events-bindings";
+import { KIND_FARM, KIND_PLOT, type RadrootsPlot } from "@radroots/events-bindings";
import type { NostrEvent } from "../../types/nostr.js";
import { get_event_tag, parse_nostr_event_basis } from "../lib.js";
import type { NostrEventBasis } from "../subscription.js";
@@ -37,7 +37,7 @@ export const parse_nostr_plot_event = (
if (farm_pubkey !== farm_ref.pubkey) return undefined;
try {
const parsed = JSON.parse(event.content);
- const plot = radroots_plot_schema.parse(parsed);
+ const plot = parsed as RadrootsPlot;
if (plot.d_tag !== d_tag) return undefined;
if (plot.farm.pubkey !== farm_ref.pubkey) return undefined;
if (plot.farm.d_tag !== farm_ref.d_tag) return undefined;
diff --git a/nostr/src/events/profile/parse.ts b/nostr/src/events/profile/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_PROFILE, radroots_profile_schema, type RadrootsProfile, type RadrootsProfileType } from "@radroots/events-bindings";
+import { KIND_PROFILE, type RadrootsProfile, type RadrootsProfileType } 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";
@@ -16,7 +16,7 @@ export const parse_nostr_profile_event = (
if (!ev) return undefined;
try {
const parsed = JSON.parse(event.content);
- const profile = radroots_profile_schema.parse(parsed);
+ const profile = parsed as RadrootsProfile;
const profile_type = parse_profile_type_tag(event.tags);
return profile_type ? { ...ev, profile, profile_type } : { ...ev, profile };
} catch {
diff --git a/nostr/src/events/reaction/parse.ts b/nostr/src/events/reaction/parse.ts
@@ -1,4 +1,4 @@
-import { KIND_REACTION, radroots_reaction_schema, type RadrootsReaction } from "@radroots/events-bindings";
+import { KIND_REACTION, type RadrootsReaction } 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";
@@ -12,7 +12,7 @@ export const parse_nostr_reaction_event = (
if (!ev) return undefined;
try {
const parsed = JSON.parse(event.content);
- const reaction = radroots_reaction_schema.parse(parsed);
+ const reaction = parsed as RadrootsReaction;
return { ...ev, reaction };
} catch {
return undefined;
diff --git a/nostr/src/index.ts b/nostr/src/index.ts
@@ -1,13 +1,3 @@
-export * from "./domain/trade/lib.js";
-export * from "./domain/trade/listing/accept/lib.js";
-export * from "./domain/trade/listing/conveyance/lib.js";
-export * from "./domain/trade/listing/fulfillment/lib.js";
-export * from "./domain/trade/listing/invoice/lib.js";
-export * from "./domain/trade/listing/order/lib.js";
-export * from "./domain/trade/listing/payment/lib.js";
-export * from "./domain/trade/listing/receipt/lib.js";
-export * from "./domain/trade/listing/tags.js";
-export * from "./domain/trade/tags.js";
export * from "./events/comment/lib.js";
export * from "./events/comment/parse.js";
export * from "./events/comment/tags.js";