web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

index.ts (4960B)


      1 export * from "./error.js";
      2 export * from "./types.js";
      3 export * from "./codec.js";
      4 
      5 import { backup_bundle_decode, backup_bundle_encode } from "./codec.js";
      6 import type {
      7     BackupBundle,
      8     BackupBundlePayload,
      9     BackupDatastoreStore,
     10     BackupKeystoreStore,
     11     BackupSqlStore
     12 } from "./types.js";
     13 import type { IWebCryptoService, KeyMaterialProvider } from "../crypto/types.js";
     14 import { DeviceKeyMaterialProvider } from "../crypto/provider.js";
     15 import { handle_err, type ResolveError } from "@radroots/utils";
     16 import { is_error } from "../utils/resolve.js";
     17 
     18 export type BackupBundleBuildOpts = {
     19     sql_store?: BackupSqlStore;
     20     keystore_store?: BackupKeystoreStore;
     21     datastore_store?: BackupDatastoreStore;
     22     app_version?: string;
     23     crypto_service?: IWebCryptoService;
     24     key_material_provider?: KeyMaterialProvider;
     25 };
     26 
     27 export type BackupBundleImportOpts = {
     28     sql_store?: BackupSqlStore;
     29     keystore_store?: BackupKeystoreStore;
     30     datastore_store?: BackupDatastoreStore;
     31     crypto_service?: IWebCryptoService;
     32     key_material_provider?: KeyMaterialProvider;
     33     import_registry?: boolean;
     34 };
     35 
     36 const collect_payloads = async (opts: BackupBundleBuildOpts): Promise<ResolveError<BackupBundlePayload[]>> => {
     37     const payloads: BackupBundlePayload[] = [];
     38     if (opts.sql_store) {
     39         const data = await opts.sql_store.export_backup();
     40         if (is_error(data)) return data;
     41         payloads.push({
     42             store_id: opts.sql_store.get_store_id(),
     43             store_type: "sql",
     44             data
     45         });
     46     }
     47     if (opts.keystore_store) {
     48         const data = await opts.keystore_store.export_backup();
     49         if (is_error(data)) return data;
     50         payloads.push({
     51             store_id: opts.keystore_store.get_store_id(),
     52             store_type: "keystore",
     53             data
     54         });
     55     }
     56     if (opts.datastore_store) {
     57         const data = await opts.datastore_store.export_backup();
     58         if (is_error(data)) return data;
     59         payloads.push({
     60             store_id: opts.datastore_store.get_store_id(),
     61             store_type: "datastore",
     62             data
     63         });
     64     }
     65     return payloads;
     66 };
     67 
     68 export const backup_bundle_build = async (opts: BackupBundleBuildOpts): Promise<ResolveError<BackupBundle>> => {
     69     const payloads = await collect_payloads(opts);
     70     if (is_error(payloads)) return payloads;
     71     const stores = payloads.map((payload) => ({
     72         store_id: payload.store_id,
     73         store_type: payload.store_type
     74     }));
     75     const crypto_registry = opts.crypto_service
     76         ? await opts.crypto_service.export_registry()
     77         : { stores: [], keys: [] };
     78     if (is_error(crypto_registry)) return crypto_registry;
     79     return {
     80         manifest: {
     81             version: 1,
     82             created_at: Date.now(),
     83             app_version: opts.app_version,
     84             stores,
     85             crypto_registry
     86         },
     87         payloads
     88     };
     89 };
     90 
     91 export const backup_bundle_export = async (opts: BackupBundleBuildOpts): Promise<ResolveError<Uint8Array>> => {
     92     try {
     93         const provider = opts.key_material_provider ?? new DeviceKeyMaterialProvider();
     94         const bundle = await backup_bundle_build(opts);
     95         if (is_error(bundle)) return bundle;
     96         return await backup_bundle_encode(bundle, provider);
     97     } catch (e) {
     98         return handle_err(e);
     99     }
    100 };
    101 
    102 export const backup_bundle_import = async (blob: Uint8Array, opts: BackupBundleImportOpts): Promise<ResolveError<BackupBundle>> => {
    103     try {
    104         const provider = opts.key_material_provider ?? new DeviceKeyMaterialProvider();
    105         const bundle = await backup_bundle_decode(blob, provider);
    106         if (opts.import_registry && opts.crypto_service) {
    107             const res = await opts.crypto_service.import_registry(bundle.manifest.crypto_registry);
    108             if (is_error(res)) return res;
    109         }
    110         for (const payload of bundle.payloads) {
    111             if (payload.store_type === "sql" && opts.sql_store) {
    112                 if (opts.sql_store.get_store_id() === payload.store_id) {
    113                     const res = await opts.sql_store.import_backup(payload.data);
    114                     if (is_error(res)) return res;
    115                 }
    116             }
    117             if (payload.store_type === "keystore" && opts.keystore_store) {
    118                 if (opts.keystore_store.get_store_id() === payload.store_id) {
    119                     const res = await opts.keystore_store.import_backup(payload.data);
    120                     if (is_error(res)) return res;
    121                 }
    122             }
    123             if (payload.store_type === "datastore" && opts.datastore_store) {
    124                 if (opts.datastore_store.get_store_id() === payload.store_id) {
    125                     const res = await opts.datastore_store.import_backup(payload.data);
    126                     if (is_error(res)) return res;
    127                 }
    128             }
    129         }
    130         return bundle;
    131     } catch (e) {
    132         return handle_err(e);
    133     }
    134 };