silosdk 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -0
- package/dist/media.cjs +135 -0
- package/dist/media.d.cts +29 -0
- package/dist/media.d.mts +29 -0
- package/dist/media.mjs +133 -0
- package/package.json +10 -3
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Silo SDK
|
|
2
|
+
|
|
3
|
+
Silo SDK provides local-first persistence primitives for Expo apps. It owns local
|
|
4
|
+
storage and returns TanStack-compatible option objects instead of wrapping
|
|
5
|
+
TanStack hooks.
|
|
6
|
+
|
|
7
|
+
Silo owns:
|
|
8
|
+
|
|
9
|
+
- SQLite persistence
|
|
10
|
+
- flat primitive document constraints
|
|
11
|
+
- defaults-first source shape
|
|
12
|
+
- settings storage
|
|
13
|
+
- media file ownership and resolution
|
|
14
|
+
|
|
15
|
+
TanStack owns:
|
|
16
|
+
|
|
17
|
+
- `createCollection()` and live collection queries
|
|
18
|
+
- `useQuery()` and `useMutation()`
|
|
19
|
+
- query and mutation state
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
yarn add silosdk @tanstack/react-db @tanstack/react-query expo-sqlite expo-file-system
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Sources
|
|
28
|
+
|
|
29
|
+
Sources are SQLite-backed document stores that produce options for TanStack DB
|
|
30
|
+
collections.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createCollection } from '@tanstack/react-db'
|
|
34
|
+
import { source } from 'silosdk/source'
|
|
35
|
+
|
|
36
|
+
type Post = {
|
|
37
|
+
title: string
|
|
38
|
+
body: string
|
|
39
|
+
published: boolean
|
|
40
|
+
coverImage: string | null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const postsSource = source<Post>('posts', {
|
|
44
|
+
title: '',
|
|
45
|
+
body: '',
|
|
46
|
+
published: false,
|
|
47
|
+
coverImage: null,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export const posts = createCollection(postsSource.collectionOptions())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Query and mutate with TanStack DB directly:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { eq, useLiveQuery } from '@tanstack/react-db'
|
|
57
|
+
|
|
58
|
+
const { data = [] } = useLiveQuery((q) =>
|
|
59
|
+
q.from({ posts }).where(({ posts }) => eq(posts.published, true)),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const tx = posts.insert({
|
|
63
|
+
id: crypto.randomUUID(),
|
|
64
|
+
title: 'Hello',
|
|
65
|
+
body: '',
|
|
66
|
+
published: false,
|
|
67
|
+
coverImage: null,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
await tx.isPersisted.promise
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Source rows are intentionally flat. Field values must be:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
type FieldValue = string | number | boolean | null
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Settings
|
|
80
|
+
|
|
81
|
+
Settings are small persisted primitive values backed by `expo-sqlite/kv-store`.
|
|
82
|
+
They produce TanStack Query options.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { setting } from 'silosdk/settings'
|
|
86
|
+
|
|
87
|
+
export const theme = setting<'system' | 'light' | 'dark'>('theme', 'system')
|
|
88
|
+
export const reduceMotion = setting('reduceMotion', false)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
93
|
+
|
|
94
|
+
const queryClient = useQueryClient()
|
|
95
|
+
const { data: value } = useQuery(theme.queryOptions())
|
|
96
|
+
const setTheme = useMutation(theme.mutationOptions({ queryClient }))
|
|
97
|
+
|
|
98
|
+
setTheme.mutate('dark')
|
|
99
|
+
setTheme.mutate((current) => (current === 'dark' ? 'light' : 'dark'))
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Setting values must also be flat primitives:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
type SettingValue = string | number | boolean | null
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Media
|
|
109
|
+
|
|
110
|
+
Media is singleton infrastructure for app-owned files. Domain documents store
|
|
111
|
+
stable media refs as plain strings, keeping source rows flat.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { media, type MediaRef } from 'silosdk/media'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Import a file with TanStack Query mutation options:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
121
|
+
|
|
122
|
+
const queryClient = useQueryClient()
|
|
123
|
+
const importMedia = useMutation(media.mutationOptions({ queryClient }))
|
|
124
|
+
|
|
125
|
+
const coverImage = await importMedia.mutateAsync({
|
|
126
|
+
uri: pickedFile.uri,
|
|
127
|
+
kind: 'image',
|
|
128
|
+
name: pickedFile.name,
|
|
129
|
+
mimeType: pickedFile.type,
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Store the returned `media://...` ref in source rows:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
posts.update(postId, (draft) => {
|
|
137
|
+
draft.coverImage = coverImage
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Resolve a media ref to a local file URI:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const { data: uri } = useQuery(media.queryOptions(post.coverImage))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`media.queryOptions()` returns `string | null`. It returns `null` when the ref is
|
|
148
|
+
empty, invalid, unknown, or missing on disk.
|
|
149
|
+
|
|
150
|
+
## API Shape
|
|
151
|
+
|
|
152
|
+
Silo exposes definition objects and option factories:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
createCollection(postsSource.collectionOptions())
|
|
156
|
+
useQuery(theme.queryOptions())
|
|
157
|
+
useMutation(theme.mutationOptions({ queryClient }))
|
|
158
|
+
useQuery(media.queryOptions(ref))
|
|
159
|
+
useMutation(media.mutationOptions({ queryClient }))
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
There is no Silo provider and no Silo hook wrapper API.
|
package/dist/media.cjs
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let expo_sqlite = require("expo-sqlite");
|
|
3
|
+
let __tanstack_react_query = require("@tanstack/react-query");
|
|
4
|
+
let expo_file_system = require("expo-file-system");
|
|
5
|
+
let nanoid = require("nanoid");
|
|
6
|
+
|
|
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, nanoid.nanoid)();
|
|
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
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
exports.isMediaRef = isMediaRef;
|
|
135
|
+
exports.media = media;
|
package/dist/media.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
|
|
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
|
+
};
|
|
23
|
+
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>;
|
|
26
|
+
};
|
|
27
|
+
declare function isMediaRef(value: unknown): value is MediaRef;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MediaData, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef, media };
|
package/dist/media.d.mts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
|
|
2
|
+
|
|
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
|
+
};
|
|
23
|
+
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>;
|
|
26
|
+
};
|
|
27
|
+
declare function isMediaRef(value: unknown): value is MediaRef;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MediaData, MediaImportInput, MediaJson, MediaKind, MediaMutationOptions, MediaQueryKey, MediaRef, isMediaRef, media };
|
package/dist/media.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { openDatabaseSync } from "expo-sqlite";
|
|
2
|
+
import { mutationOptions, queryOptions } from "@tanstack/react-query";
|
|
3
|
+
import { Directory, File, Paths } from "expo-file-system";
|
|
4
|
+
import { nanoid } from "nanoid";
|
|
5
|
+
|
|
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 = nanoid();
|
|
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
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { isMediaRef, media };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silosdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsdown",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"@tanstack/react-query": "^5.101.0",
|
|
18
18
|
"@types/node": "^25.3.0",
|
|
19
19
|
"@types/react": "^19",
|
|
20
|
+
"expo-file-system": "^56.0.8",
|
|
20
21
|
"expo-sqlite": "*",
|
|
21
22
|
"prettier": "^3.7.4",
|
|
22
23
|
"react-native": "0.85.0",
|
|
@@ -26,11 +27,12 @@
|
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@standard-schema/spec": "^1.0.0",
|
|
29
|
-
"@tanstack/react-db": "^0.1.86",
|
|
30
|
-
"@tanstack/react-query": "^5.101.0",
|
|
31
30
|
"nanoid": "5.1.6"
|
|
32
31
|
},
|
|
33
32
|
"peerDependencies": {
|
|
33
|
+
"@tanstack/react-db": "^0.1.86",
|
|
34
|
+
"@tanstack/react-query": "^5.101.0",
|
|
35
|
+
"expo-file-system": "^56.0.8",
|
|
34
36
|
"expo-sqlite": "*",
|
|
35
37
|
"react": "^19",
|
|
36
38
|
"react-native": "0.85.x"
|
|
@@ -56,6 +58,11 @@
|
|
|
56
58
|
"import": "./dist/settings.mjs",
|
|
57
59
|
"require": "./dist/settings.cjs"
|
|
58
60
|
},
|
|
61
|
+
"./media": {
|
|
62
|
+
"types": "./dist/media.d.mts",
|
|
63
|
+
"import": "./dist/media.mjs",
|
|
64
|
+
"require": "./dist/media.cjs"
|
|
65
|
+
},
|
|
59
66
|
"./package.json": "./package.json"
|
|
60
67
|
}
|
|
61
68
|
}
|