silosdk 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +29 -5
  2. package/dist/internal/indexed-db.cjs +44 -0
  3. package/dist/internal/indexed-db.mjs +41 -0
  4. package/dist/media/shared.cjs +99 -0
  5. package/dist/media/shared.d.cts +26 -0
  6. package/dist/media/shared.d.mts +26 -0
  7. package/dist/media/shared.mjs +93 -0
  8. package/dist/media/storage.native.cjs +102 -0
  9. package/dist/media/storage.native.mjs +101 -0
  10. package/dist/media/storage.web.cjs +79 -0
  11. package/dist/media/storage.web.mjs +78 -0
  12. package/dist/media.cjs +5 -131
  13. package/dist/media.d.cts +8 -25
  14. package/dist/media.d.mts +8 -25
  15. package/dist/media.mjs +4 -129
  16. package/dist/media.web.cjs +9 -0
  17. package/dist/media.web.d.cts +12 -0
  18. package/dist/media.web.d.mts +12 -0
  19. package/dist/media.web.mjs +8 -0
  20. package/dist/settings/shared.cjs +63 -0
  21. package/dist/settings/shared.d.cts +19 -0
  22. package/dist/settings/shared.d.mts +19 -0
  23. package/dist/settings/shared.mjs +59 -0
  24. package/dist/settings/storage.native.cjs +24 -0
  25. package/dist/settings/storage.native.mjs +22 -0
  26. package/dist/settings/storage.web.cjs +34 -0
  27. package/dist/settings/storage.web.mjs +34 -0
  28. package/dist/settings.cjs +4 -66
  29. package/dist/settings.d.cts +4 -18
  30. package/dist/settings.d.mts +4 -18
  31. package/dist/settings.mjs +4 -64
  32. package/dist/settings.web.cjs +8 -0
  33. package/dist/settings.web.d.cts +6 -0
  34. package/dist/settings.web.d.mts +6 -0
  35. package/dist/settings.web.mjs +8 -0
  36. package/dist/store/shared.cjs +338 -0
  37. package/dist/store/shared.d.cts +58 -0
  38. package/dist/store/shared.d.mts +58 -0
  39. package/dist/store/shared.mjs +333 -0
  40. package/dist/store/storage.native.cjs +133 -0
  41. package/dist/store/storage.native.mjs +132 -0
  42. package/dist/store/storage.web.cjs +142 -0
  43. package/dist/store/storage.web.mjs +142 -0
  44. package/dist/store.cjs +4 -421
  45. package/dist/store.d.cts +4 -58
  46. package/dist/store.d.mts +4 -58
  47. package/dist/store.mjs +4 -421
  48. package/dist/store.web.cjs +15 -0
  49. package/dist/store.web.d.cts +7 -0
  50. package/dist/store.web.d.mts +7 -0
  51. package/dist/store.web.mjs +12 -0
  52. package/package.json +31 -1
@@ -0,0 +1,78 @@
1
+ import { createIndexedDbOpener, requestResult, transactionDone } from "../internal/indexed-db.mjs";
2
+ import { createMediaRef, extensionForMimeType } from "./shared.mjs";
3
+ import { randomUUID } from "expo-crypto";
4
+
5
+ //#region src/media/storage.web.ts
6
+ const objectUrls = /* @__PURE__ */ new Map();
7
+ const mediaStorage = {
8
+ importMedia,
9
+ resolveMediaUri,
10
+ deleteMedia
11
+ };
12
+ const openSiloMediaIndexedDb = createIndexedDbOpener("silo-media", 1, (db) => {
13
+ if (!db.objectStoreNames.contains("media_files")) db.createObjectStore("media_files", { keyPath: "ref" });
14
+ if (!db.objectStoreNames.contains("media_blobs")) db.createObjectStore("media_blobs", { keyPath: "ref" });
15
+ });
16
+ async function importMedia(input) {
17
+ const id = randomUUID();
18
+ const ref = createMediaRef(input.kind, id);
19
+ const blob = await (await fetch(input.uri)).blob();
20
+ const name = input.name ?? `${id}${extensionForMimeType(input.mimeType ?? blob.type)}`;
21
+ const mimeType = input.mimeType ?? (blob.type || null);
22
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
23
+ const transaction = (await openSiloMediaIndexedDb()).transaction(["media_files", "media_blobs"], "readwrite");
24
+ transaction.objectStore("media_files").put({
25
+ ref,
26
+ uri: ref,
27
+ kind: input.kind,
28
+ name,
29
+ mimeType,
30
+ size: blob.size,
31
+ createdAt: timestamp,
32
+ data: JSON.stringify(input.data ?? {})
33
+ });
34
+ transaction.objectStore("media_blobs").put({
35
+ ref,
36
+ blob
37
+ });
38
+ await transactionDone(transaction);
39
+ const uri = URL.createObjectURL(blob);
40
+ objectUrls.set(ref, uri);
41
+ return {
42
+ ref,
43
+ uri
44
+ };
45
+ }
46
+ async function resolveMediaUri(ref) {
47
+ const existingUrl = objectUrls.get(ref);
48
+ if (existingUrl) return existingUrl;
49
+ const transaction = (await openSiloMediaIndexedDb()).transaction("media_blobs", "readonly");
50
+ const done = transactionDone(transaction);
51
+ const row = await requestResult(transaction.objectStore("media_blobs").get(ref));
52
+ await done;
53
+ if (!row) return null;
54
+ const uri = URL.createObjectURL(row.blob);
55
+ objectUrls.set(ref, uri);
56
+ return uri;
57
+ }
58
+ async function deleteMedia(ref) {
59
+ const db = await openSiloMediaIndexedDb();
60
+ const readTransaction = db.transaction("media_files", "readonly");
61
+ const readDone = transactionDone(readTransaction);
62
+ const existing = await requestResult(readTransaction.objectStore("media_files").get(ref));
63
+ await readDone;
64
+ if (!existing) return false;
65
+ const transaction = db.transaction(["media_files", "media_blobs"], "readwrite");
66
+ transaction.objectStore("media_files").delete(ref);
67
+ transaction.objectStore("media_blobs").delete(ref);
68
+ await transactionDone(transaction);
69
+ const objectUrl = objectUrls.get(ref);
70
+ if (objectUrl) {
71
+ URL.revokeObjectURL(objectUrl);
72
+ objectUrls.delete(ref);
73
+ }
74
+ return true;
75
+ }
76
+
77
+ //#endregion
78
+ export { mediaStorage };
package/dist/media.cjs CHANGED
@@ -1,135 +1,9 @@
1
- const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
- let expo_crypto = require("expo-crypto");
3
- let expo_sqlite = require("expo-sqlite");
4
- let __tanstack_react_query = require("@tanstack/react-query");
5
- let expo_file_system = require("expo-file-system");
1
+ const require_shared = require('./media/shared.cjs');
2
+ const require_storage_native = require('./media/storage.native.cjs');
6
3
 
7
- //#region src/media/index.ts
8
- const media = {
9
- queryOptions(ref) {
10
- return (0, __tanstack_react_query.queryOptions)({
11
- queryKey: mediaQueryKey(ref),
12
- queryFn: () => resolveMediaUri(ref)
13
- });
14
- },
15
- mutationOptions(options = {}) {
16
- return (0, __tanstack_react_query.mutationOptions)({
17
- mutationKey: [
18
- "silo",
19
- "media",
20
- "import"
21
- ],
22
- mutationFn: async (input) => {
23
- const imported = await importMedia(input);
24
- options.queryClient?.setQueryData(mediaQueryKey(imported.ref), imported.uri);
25
- return imported.ref;
26
- }
27
- });
28
- }
29
- };
30
- function isMediaRef(value) {
31
- return typeof value === "string" && value.startsWith("media://");
32
- }
33
- function mediaQueryKey(ref) {
34
- return [
35
- "silo",
36
- "media",
37
- ref ?? null
38
- ];
39
- }
40
- async function importMedia(input) {
41
- assertMediaKind(input.kind);
42
- const id = (0, expo_crypto.randomUUID)();
43
- const ref = createMediaRef(input.kind, id);
44
- const source = new expo_file_system.File(input.uri);
45
- const name = input.name ?? source.name ?? `${id}${source.extension}`;
46
- const destinationDirectory = mediaDirectory(input.kind);
47
- destinationDirectory.create({
48
- idempotent: true,
49
- intermediates: true
50
- });
51
- const destination = new expo_file_system.File(destinationDirectory, `${id}${extensionFor(name)}`);
52
- await source.copy(destination);
53
- const info = destination.info();
54
- const mimeType = input.mimeType ?? (destination.type || null);
55
- const size = info.size ?? destination.size;
56
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
57
- insertMediaFile({
58
- ref,
59
- uri: destination.uri,
60
- kind: input.kind,
61
- name,
62
- mimeType,
63
- size,
64
- createdAt: timestamp,
65
- data: input.data ?? {}
66
- });
67
- return {
68
- ref,
69
- uri: destination.uri
70
- };
71
- }
72
- function resolveMediaUri(ref) {
73
- if (!isMediaRef(ref)) return null;
74
- const row = getDatabase().getFirstSync(`SELECT ref, uri FROM media_files WHERE ref = ?`, [ref]);
75
- if (!row) return null;
76
- return new expo_file_system.File(row.uri).exists ? row.uri : null;
77
- }
78
- function createMediaRef(kind, id) {
79
- return `media://silo/${kindDirectoryName(kind)}/${id}`;
80
- }
81
- function mediaDirectory(kind) {
82
- return new expo_file_system.Directory(expo_file_system.Paths.document, "silo", "media", kindDirectoryName(kind));
83
- }
84
- function kindDirectoryName(kind) {
85
- switch (kind) {
86
- case "image": return "images";
87
- case "video": return "videos";
88
- case "audio": return "audio";
89
- case "file": return "files";
90
- }
91
- }
92
- function extensionFor(name) {
93
- return name.match(/\.[A-Za-z0-9]+$/)?.[0] ?? "";
94
- }
95
- function assertMediaKind(kind) {
96
- if (kind !== "image" && kind !== "video" && kind !== "audio" && kind !== "file") throw new Error("Media kind must be one of \"image\", \"video\", \"audio\", or \"file\".");
97
- }
98
- let database;
99
- function getDatabase() {
100
- if (!database) {
101
- database = (0, expo_sqlite.openDatabaseSync)("silo.db");
102
- database.execSync(`
103
- PRAGMA journal_mode = WAL;
104
-
105
- CREATE TABLE IF NOT EXISTS media_files (
106
- ref TEXT PRIMARY KEY,
107
- uri TEXT NOT NULL,
108
- kind TEXT NOT NULL,
109
- name TEXT NOT NULL,
110
- mimeType TEXT,
111
- size INTEGER NOT NULL,
112
- createdAt TEXT NOT NULL,
113
- data TEXT NOT NULL
114
- );
115
- `);
116
- }
117
- return database;
118
- }
119
- function insertMediaFile(file) {
120
- getDatabase().runSync(`INSERT INTO media_files (ref, uri, kind, name, mimeType, size, createdAt, data)
121
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
122
- file.ref,
123
- file.uri,
124
- file.kind,
125
- file.name,
126
- file.mimeType,
127
- file.size,
128
- file.createdAt,
129
- JSON.stringify(file.data)
130
- ]);
131
- }
4
+ //#region src/media/index.native.ts
5
+ const media = require_shared.createMediaApi(require_storage_native.mediaStorage);
132
6
 
133
7
  //#endregion
134
- exports.isMediaRef = isMediaRef;
8
+ exports.isMediaRef = require_shared.isMediaRef;
135
9
  exports.media = media;
package/dist/media.d.cts CHANGED
@@ -1,29 +1,12 @@
1
- import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
1
+ import { MediaData, MediaDeleteInput, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef } from "./media/shared.cjs";
2
+ import * as _tanstack_react_query2 from "@tanstack/react-query";
2
3
 
3
- //#region src/media/index.d.ts
4
- type MediaRef = `media://${string}`;
5
- type MediaKind = 'image' | 'video' | 'audio' | 'file';
6
- type MediaJson = string | number | boolean | null | MediaJson[] | {
7
- [key: string]: MediaJson;
8
- };
9
- type MediaData = {
10
- [key: string]: MediaJson;
11
- };
12
- type MediaImportInput = {
13
- uri: string;
14
- kind: MediaKind;
15
- name?: string;
16
- mimeType?: string | null;
17
- data?: MediaData;
18
- };
19
- type MediaQueryKey = readonly ['silo', 'media', MediaRef | string | null];
20
- type MediaMutationOptions = {
21
- queryClient?: QueryClient;
22
- };
4
+ //#region src/media/index.native.d.ts
23
5
  declare const media: {
24
- queryOptions(ref: MediaRef | string | null | undefined): UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
25
- mutationOptions(options?: MediaMutationOptions): UseMutationOptions<MediaRef, Error, MediaImportInput>;
6
+ queryOptions(ref: MediaRef | string | null | undefined): _tanstack_react_query2.UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
7
+ mutationOptions(options?: MediaMutationOptions): _tanstack_react_query2.UseMutationOptions<MediaRef, Error, MediaImportInput>;
8
+ delete(ref: MediaDeleteInput): Promise<boolean>;
9
+ deleteMutationOptions(options?: MediaMutationOptions): _tanstack_react_query2.UseMutationOptions<boolean, Error, MediaDeleteInput>;
26
10
  };
27
- declare function isMediaRef(value: unknown): value is MediaRef;
28
11
  //#endregion
29
- export { MediaData, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef, media };
12
+ export { type MediaData, type MediaDeleteInput, type MediaImportInput, type MediaJson, type MediaKind, type MediaMutationOptions, type MediaQueryKey, type MediaRef, isMediaRef, media };
package/dist/media.d.mts CHANGED
@@ -1,29 +1,12 @@
1
- import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
1
+ import { MediaData, MediaDeleteInput, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef } from "./media/shared.mjs";
2
+ import * as _tanstack_react_query2 from "@tanstack/react-query";
2
3
 
3
- //#region src/media/index.d.ts
4
- type MediaRef = `media://${string}`;
5
- type MediaKind = 'image' | 'video' | 'audio' | 'file';
6
- type MediaJson = string | number | boolean | null | MediaJson[] | {
7
- [key: string]: MediaJson;
8
- };
9
- type MediaData = {
10
- [key: string]: MediaJson;
11
- };
12
- type MediaImportInput = {
13
- uri: string;
14
- kind: MediaKind;
15
- name?: string;
16
- mimeType?: string | null;
17
- data?: MediaData;
18
- };
19
- type MediaQueryKey = readonly ['silo', 'media', MediaRef | string | null];
20
- type MediaMutationOptions = {
21
- queryClient?: QueryClient;
22
- };
4
+ //#region src/media/index.native.d.ts
23
5
  declare const media: {
24
- queryOptions(ref: MediaRef | string | null | undefined): UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
25
- mutationOptions(options?: MediaMutationOptions): UseMutationOptions<MediaRef, Error, MediaImportInput>;
6
+ queryOptions(ref: MediaRef | string | null | undefined): _tanstack_react_query2.UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
7
+ mutationOptions(options?: MediaMutationOptions): _tanstack_react_query2.UseMutationOptions<MediaRef, Error, MediaImportInput>;
8
+ delete(ref: MediaDeleteInput): Promise<boolean>;
9
+ deleteMutationOptions(options?: MediaMutationOptions): _tanstack_react_query2.UseMutationOptions<boolean, Error, MediaDeleteInput>;
26
10
  };
27
- declare function isMediaRef(value: unknown): value is MediaRef;
28
11
  //#endregion
29
- export { MediaData, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef, media };
12
+ export { type MediaData, type MediaDeleteInput, type MediaImportInput, type MediaJson, type MediaKind, type MediaMutationOptions, type MediaQueryKey, type MediaRef, isMediaRef, media };
package/dist/media.mjs CHANGED
@@ -1,133 +1,8 @@
1
- import { randomUUID } from "expo-crypto";
2
- import { openDatabaseSync } from "expo-sqlite";
3
- import { mutationOptions, queryOptions } from "@tanstack/react-query";
4
- import { Directory, File, Paths } from "expo-file-system";
1
+ import { createMediaApi, isMediaRef } from "./media/shared.mjs";
2
+ import { mediaStorage } from "./media/storage.native.mjs";
5
3
 
6
- //#region src/media/index.ts
7
- const media = {
8
- queryOptions(ref) {
9
- return queryOptions({
10
- queryKey: mediaQueryKey(ref),
11
- queryFn: () => resolveMediaUri(ref)
12
- });
13
- },
14
- mutationOptions(options = {}) {
15
- return mutationOptions({
16
- mutationKey: [
17
- "silo",
18
- "media",
19
- "import"
20
- ],
21
- mutationFn: async (input) => {
22
- const imported = await importMedia(input);
23
- options.queryClient?.setQueryData(mediaQueryKey(imported.ref), imported.uri);
24
- return imported.ref;
25
- }
26
- });
27
- }
28
- };
29
- function isMediaRef(value) {
30
- return typeof value === "string" && value.startsWith("media://");
31
- }
32
- function mediaQueryKey(ref) {
33
- return [
34
- "silo",
35
- "media",
36
- ref ?? null
37
- ];
38
- }
39
- async function importMedia(input) {
40
- assertMediaKind(input.kind);
41
- const id = randomUUID();
42
- const ref = createMediaRef(input.kind, id);
43
- const source = new File(input.uri);
44
- const name = input.name ?? source.name ?? `${id}${source.extension}`;
45
- const destinationDirectory = mediaDirectory(input.kind);
46
- destinationDirectory.create({
47
- idempotent: true,
48
- intermediates: true
49
- });
50
- const destination = new File(destinationDirectory, `${id}${extensionFor(name)}`);
51
- await source.copy(destination);
52
- const info = destination.info();
53
- const mimeType = input.mimeType ?? (destination.type || null);
54
- const size = info.size ?? destination.size;
55
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
56
- insertMediaFile({
57
- ref,
58
- uri: destination.uri,
59
- kind: input.kind,
60
- name,
61
- mimeType,
62
- size,
63
- createdAt: timestamp,
64
- data: input.data ?? {}
65
- });
66
- return {
67
- ref,
68
- uri: destination.uri
69
- };
70
- }
71
- function resolveMediaUri(ref) {
72
- if (!isMediaRef(ref)) return null;
73
- const row = getDatabase().getFirstSync(`SELECT ref, uri FROM media_files WHERE ref = ?`, [ref]);
74
- if (!row) return null;
75
- return new File(row.uri).exists ? row.uri : null;
76
- }
77
- function createMediaRef(kind, id) {
78
- return `media://silo/${kindDirectoryName(kind)}/${id}`;
79
- }
80
- function mediaDirectory(kind) {
81
- return new Directory(Paths.document, "silo", "media", kindDirectoryName(kind));
82
- }
83
- function kindDirectoryName(kind) {
84
- switch (kind) {
85
- case "image": return "images";
86
- case "video": return "videos";
87
- case "audio": return "audio";
88
- case "file": return "files";
89
- }
90
- }
91
- function extensionFor(name) {
92
- return name.match(/\.[A-Za-z0-9]+$/)?.[0] ?? "";
93
- }
94
- function assertMediaKind(kind) {
95
- if (kind !== "image" && kind !== "video" && kind !== "audio" && kind !== "file") throw new Error("Media kind must be one of \"image\", \"video\", \"audio\", or \"file\".");
96
- }
97
- let database;
98
- function getDatabase() {
99
- if (!database) {
100
- database = openDatabaseSync("silo.db");
101
- database.execSync(`
102
- PRAGMA journal_mode = WAL;
103
-
104
- CREATE TABLE IF NOT EXISTS media_files (
105
- ref TEXT PRIMARY KEY,
106
- uri TEXT NOT NULL,
107
- kind TEXT NOT NULL,
108
- name TEXT NOT NULL,
109
- mimeType TEXT,
110
- size INTEGER NOT NULL,
111
- createdAt TEXT NOT NULL,
112
- data TEXT NOT NULL
113
- );
114
- `);
115
- }
116
- return database;
117
- }
118
- function insertMediaFile(file) {
119
- getDatabase().runSync(`INSERT INTO media_files (ref, uri, kind, name, mimeType, size, createdAt, data)
120
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
121
- file.ref,
122
- file.uri,
123
- file.kind,
124
- file.name,
125
- file.mimeType,
126
- file.size,
127
- file.createdAt,
128
- JSON.stringify(file.data)
129
- ]);
130
- }
4
+ //#region src/media/index.native.ts
5
+ const media = createMediaApi(mediaStorage);
131
6
 
132
7
  //#endregion
133
8
  export { isMediaRef, media };
@@ -0,0 +1,9 @@
1
+ const require_shared = require('./media/shared.cjs');
2
+ const require_storage_web = require('./media/storage.web.cjs');
3
+
4
+ //#region src/media/index.web.ts
5
+ const media = require_shared.createMediaApi(require_storage_web.mediaStorage);
6
+
7
+ //#endregion
8
+ exports.isMediaRef = require_shared.isMediaRef;
9
+ exports.media = media;
@@ -0,0 +1,12 @@
1
+ import { MediaData, MediaDeleteInput, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef } from "./media/shared.cjs";
2
+ import * as _tanstack_react_query0 from "@tanstack/react-query";
3
+
4
+ //#region src/media/index.web.d.ts
5
+ declare const media: {
6
+ queryOptions(ref: MediaRef | string | null | undefined): _tanstack_react_query0.UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
7
+ mutationOptions(options?: MediaMutationOptions): _tanstack_react_query0.UseMutationOptions<MediaRef, Error, MediaImportInput>;
8
+ delete(ref: MediaDeleteInput): Promise<boolean>;
9
+ deleteMutationOptions(options?: MediaMutationOptions): _tanstack_react_query0.UseMutationOptions<boolean, Error, MediaDeleteInput>;
10
+ };
11
+ //#endregion
12
+ export { type MediaData, type MediaDeleteInput, type MediaImportInput, type MediaJson, type MediaKind, type MediaMutationOptions, type MediaQueryKey, type MediaRef, isMediaRef, media };
@@ -0,0 +1,12 @@
1
+ import { MediaData, MediaDeleteInput, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef } from "./media/shared.mjs";
2
+ import * as _tanstack_react_query0 from "@tanstack/react-query";
3
+
4
+ //#region src/media/index.web.d.ts
5
+ declare const media: {
6
+ queryOptions(ref: MediaRef | string | null | undefined): _tanstack_react_query0.UseQueryOptions<string | null, Error, string | null, MediaQueryKey>;
7
+ mutationOptions(options?: MediaMutationOptions): _tanstack_react_query0.UseMutationOptions<MediaRef, Error, MediaImportInput>;
8
+ delete(ref: MediaDeleteInput): Promise<boolean>;
9
+ deleteMutationOptions(options?: MediaMutationOptions): _tanstack_react_query0.UseMutationOptions<boolean, Error, MediaDeleteInput>;
10
+ };
11
+ //#endregion
12
+ export { type MediaData, type MediaDeleteInput, type MediaImportInput, type MediaJson, type MediaKind, type MediaMutationOptions, type MediaQueryKey, type MediaRef, isMediaRef, media };
@@ -0,0 +1,8 @@
1
+ import { createMediaApi, isMediaRef } from "./media/shared.mjs";
2
+ import { mediaStorage } from "./media/storage.web.mjs";
3
+
4
+ //#region src/media/index.web.ts
5
+ const media = createMediaApi(mediaStorage);
6
+
7
+ //#endregion
8
+ export { isMediaRef, media };
@@ -0,0 +1,63 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let __tanstack_react_query = require("@tanstack/react-query");
3
+
4
+ //#region src/settings/shared.ts
5
+ const settingNames = /* @__PURE__ */ new Set();
6
+ const settingNamePattern = /^[A-Za-z][A-Za-z0-9_.:-]*$/;
7
+ function createSettingApi(storage) {
8
+ function setting(name, defaultValue) {
9
+ assertSettingName(name);
10
+ assertSettingValue(resolveDefault(defaultValue));
11
+ if (settingNames.has(name)) throw new Error(`Setting "${name}" is already registered. Define each setting once and import the existing setting instead.`);
12
+ settingNames.add(name);
13
+ const queryKey = [
14
+ "silo",
15
+ "setting",
16
+ name
17
+ ];
18
+ return {
19
+ name,
20
+ defaultValue,
21
+ queryKey,
22
+ queryOptions() {
23
+ return (0, __tanstack_react_query.queryOptions)({
24
+ queryKey,
25
+ queryFn: () => storage.read(name, defaultValue)
26
+ });
27
+ },
28
+ mutationOptions(options = {}) {
29
+ return (0, __tanstack_react_query.mutationOptions)({
30
+ mutationKey: queryKey,
31
+ mutationFn: async (valueOrUpdater) => {
32
+ const current = await storage.read(name, defaultValue);
33
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(current) : valueOrUpdater;
34
+ await storage.write(name, next);
35
+ return next;
36
+ },
37
+ onSuccess(value) {
38
+ options.queryClient?.setQueryData(queryKey, value);
39
+ }
40
+ });
41
+ }
42
+ };
43
+ }
44
+ return { setting };
45
+ }
46
+ function storageKey(name) {
47
+ return `silo:setting:${name}`;
48
+ }
49
+ function resolveDefault(defaultValue) {
50
+ return typeof defaultValue === "function" ? defaultValue() : defaultValue;
51
+ }
52
+ function assertSettingValue(value) {
53
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && value !== null) throw new Error("Settings must be strings, numbers, booleans, or null.");
54
+ }
55
+ function assertSettingName(name) {
56
+ if (!settingNamePattern.test(name)) throw new Error(`Setting names must start with a letter and contain only letters, numbers, underscores, dots, colons, or hyphens. Received "${name}".`);
57
+ }
58
+
59
+ //#endregion
60
+ exports.assertSettingValue = assertSettingValue;
61
+ exports.createSettingApi = createSettingApi;
62
+ exports.resolveDefault = resolveDefault;
63
+ exports.storageKey = storageKey;
@@ -0,0 +1,19 @@
1
+ import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
+
3
+ //#region src/settings/shared.d.ts
4
+ type SettingValue = string | number | boolean | null;
5
+ type SettingDefault<T extends SettingValue> = T | (() => T);
6
+ type SettingMutation<T extends SettingValue> = T | ((current: T) => T);
7
+ type SettingQueryKey = readonly ['silo', 'setting', string];
8
+ type SettingMutationOptions = {
9
+ queryClient?: QueryClient;
10
+ };
11
+ type Setting<T extends SettingValue> = {
12
+ readonly name: string;
13
+ readonly defaultValue: SettingDefault<T>;
14
+ readonly queryKey: SettingQueryKey;
15
+ queryOptions(): UseQueryOptions<T, Error, T, SettingQueryKey>;
16
+ mutationOptions(options?: SettingMutationOptions): UseMutationOptions<T, Error, SettingMutation<T>>;
17
+ };
18
+ //#endregion
19
+ export { Setting, SettingDefault, SettingMutation, SettingMutationOptions, SettingQueryKey, SettingValue };
@@ -0,0 +1,19 @@
1
+ import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
+
3
+ //#region src/settings/shared.d.ts
4
+ type SettingValue = string | number | boolean | null;
5
+ type SettingDefault<T extends SettingValue> = T | (() => T);
6
+ type SettingMutation<T extends SettingValue> = T | ((current: T) => T);
7
+ type SettingQueryKey = readonly ['silo', 'setting', string];
8
+ type SettingMutationOptions = {
9
+ queryClient?: QueryClient;
10
+ };
11
+ type Setting<T extends SettingValue> = {
12
+ readonly name: string;
13
+ readonly defaultValue: SettingDefault<T>;
14
+ readonly queryKey: SettingQueryKey;
15
+ queryOptions(): UseQueryOptions<T, Error, T, SettingQueryKey>;
16
+ mutationOptions(options?: SettingMutationOptions): UseMutationOptions<T, Error, SettingMutation<T>>;
17
+ };
18
+ //#endregion
19
+ export { Setting, SettingDefault, SettingMutation, SettingMutationOptions, SettingQueryKey, SettingValue };
@@ -0,0 +1,59 @@
1
+ import { mutationOptions, queryOptions } from "@tanstack/react-query";
2
+
3
+ //#region src/settings/shared.ts
4
+ const settingNames = /* @__PURE__ */ new Set();
5
+ const settingNamePattern = /^[A-Za-z][A-Za-z0-9_.:-]*$/;
6
+ function createSettingApi(storage) {
7
+ function setting(name, defaultValue) {
8
+ assertSettingName(name);
9
+ assertSettingValue(resolveDefault(defaultValue));
10
+ if (settingNames.has(name)) throw new Error(`Setting "${name}" is already registered. Define each setting once and import the existing setting instead.`);
11
+ settingNames.add(name);
12
+ const queryKey = [
13
+ "silo",
14
+ "setting",
15
+ name
16
+ ];
17
+ return {
18
+ name,
19
+ defaultValue,
20
+ queryKey,
21
+ queryOptions() {
22
+ return queryOptions({
23
+ queryKey,
24
+ queryFn: () => storage.read(name, defaultValue)
25
+ });
26
+ },
27
+ mutationOptions(options = {}) {
28
+ return mutationOptions({
29
+ mutationKey: queryKey,
30
+ mutationFn: async (valueOrUpdater) => {
31
+ const current = await storage.read(name, defaultValue);
32
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(current) : valueOrUpdater;
33
+ await storage.write(name, next);
34
+ return next;
35
+ },
36
+ onSuccess(value) {
37
+ options.queryClient?.setQueryData(queryKey, value);
38
+ }
39
+ });
40
+ }
41
+ };
42
+ }
43
+ return { setting };
44
+ }
45
+ function storageKey(name) {
46
+ return `silo:setting:${name}`;
47
+ }
48
+ function resolveDefault(defaultValue) {
49
+ return typeof defaultValue === "function" ? defaultValue() : defaultValue;
50
+ }
51
+ function assertSettingValue(value) {
52
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && value !== null) throw new Error("Settings must be strings, numbers, booleans, or null.");
53
+ }
54
+ function assertSettingName(name) {
55
+ if (!settingNamePattern.test(name)) throw new Error(`Setting names must start with a letter and contain only letters, numbers, underscores, dots, colons, or hyphens. Received "${name}".`);
56
+ }
57
+
58
+ //#endregion
59
+ export { assertSettingValue, createSettingApi, resolveDefault, storageKey };
@@ -0,0 +1,24 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_shared = require('./shared.cjs');
3
+ let expo_sqlite_kv_store = require("expo-sqlite/kv-store");
4
+ expo_sqlite_kv_store = require_rolldown_runtime.__toESM(expo_sqlite_kv_store);
5
+
6
+ //#region src/settings/storage.native.ts
7
+ const settingsStorage = {
8
+ read,
9
+ write
10
+ };
11
+ function read(name, defaultValue) {
12
+ const stored = expo_sqlite_kv_store.default.getItemSync(require_shared.storageKey(name));
13
+ if (stored === null) return require_shared.resolveDefault(defaultValue);
14
+ const value = JSON.parse(stored);
15
+ require_shared.assertSettingValue(value);
16
+ return value;
17
+ }
18
+ function write(name, value) {
19
+ require_shared.assertSettingValue(value);
20
+ expo_sqlite_kv_store.default.setItemSync(require_shared.storageKey(name), JSON.stringify(value));
21
+ }
22
+
23
+ //#endregion
24
+ exports.settingsStorage = settingsStorage;