@party-stack/expo-runtime 0.4.1 → 0.5.0
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 +81 -0
- package/lib/ExpoFileSystemBlobBytesStore.d.ts +7 -6
- package/lib/ExpoFileSystemBlobBytesStore.d.ts.map +1 -1
- package/lib/ExpoFileSystemBlobBytesStore.js +24 -15
- package/lib/ExpoFileSystemBlobBytesStore.js.map +1 -1
- package/lib/ExpoKeyMaterialStore.d.ts +5 -0
- package/lib/ExpoKeyMaterialStore.d.ts.map +1 -0
- package/lib/ExpoKeyMaterialStore.js +27 -0
- package/lib/ExpoKeyMaterialStore.js.map +1 -0
- package/lib/ExpoNetworkConnectivity.d.ts +6 -12
- package/lib/ExpoNetworkConnectivity.d.ts.map +1 -1
- package/lib/ExpoNetworkConnectivity.js +20 -34
- package/lib/ExpoNetworkConnectivity.js.map +1 -1
- package/lib/createExpoBrowserAuthentication.d.ts.map +1 -1
- package/lib/createExpoBrowserAuthentication.js +31 -12
- package/lib/createExpoBrowserAuthentication.js.map +1 -1
- package/lib/createExpoRuntime.d.ts +9 -3
- package/lib/createExpoRuntime.d.ts.map +1 -1
- package/lib/createExpoRuntime.js +107 -27
- package/lib/createExpoRuntime.js.map +1 -1
- package/lib/encryptionKeys.d.ts +6 -0
- package/lib/encryptionKeys.d.ts.map +1 -0
- package/lib/encryptionKeys.js +22 -0
- package/lib/encryptionKeys.js.map +1 -0
- package/lib/hashStorageName.d.ts +4 -0
- package/lib/hashStorageName.d.ts.map +1 -0
- package/lib/hashStorageName.js +9 -0
- package/lib/hashStorageName.js.map +1 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -2
- package/lib/polyfill.d.ts +1 -0
- package/lib/polyfill.js +46 -0
- package/lib/polyfill.js.map +1 -0
- package/package.json +31 -13
- package/plugin/index.cjs +22 -0
- package/plugin/index.d.cts +9 -0
- package/lib/ExpoSecretStore.d.ts +0 -12
- package/lib/ExpoSecretStore.d.ts.map +0 -1
- package/lib/ExpoSecretStore.js +0 -28
- package/lib/ExpoSecretStore.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Expo runtime
|
|
2
|
+
|
|
3
|
+
This runtime targets Expo SDK 57. Install `@party-stack/expo-runtime` and its Expo peer dependencies using SDK 57-compatible versions. Use Expo's installed native fetch; no separate `expo-fetch` package or fetch installer is needed. The runtime includes `expo-blob`.
|
|
4
|
+
|
|
5
|
+
Import the required polyfill first in your application entry point, before importing application code or creating clients:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import "@party-stack/expo-runtime/polyfill";
|
|
9
|
+
import "expo-router/entry";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Use the lazy `expoRuntime()` layer from `@party-stack/expo-runtime` for native persistence:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { expoRuntime } from "@party-stack/expo-runtime";
|
|
16
|
+
|
|
17
|
+
const runtime = expoRuntime(); // persistent SQLite, filesystem bytes, SecureStore keys
|
|
18
|
+
const uiRuntime = expoRuntime({ storage: "memory" });
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`storage: "memory"` uses memory tables, attachment bytes, and ephemeral native encryption
|
|
22
|
+
keys. It retains Expo browser authentication and network connectivity without opening
|
|
23
|
+
SQLite, creating filesystem directories, or accessing SecureStore. This is useful for
|
|
24
|
+
`AuthenticationProvider` and a memory UI replica that needs Expo's platform capabilities.
|
|
25
|
+
Both modes acquire partitions lazily and release their NetInfo listeners with the scope.
|
|
26
|
+
|
|
27
|
+
For a durable parent plus a memory UI replica, build this layer in the application's Effect scope, provide its context while constructing the ontology server, then connect with `localClient(server)` from `@party-stack/ontology/local`. Use `expoRuntime({ storage: "memory" })` for React views that need Expo's native browser authentication without persistent UI storage. The [Expo guide](../../apps/website/content/docs/expo.mdx) shows Connect sign-in, saved Foundry accounts, ontology selection, object browsing, and background checkpoints through one shared device owner. Passing the default persistent runtime directly to `OntologyProvider` or `createLiveOntology` instead creates one persistent UI replica.
|
|
28
|
+
|
|
29
|
+
The polyfill replaces global `Blob` with Expo Blob and global `File` with a compatible class extending Expo Blob. It also supplies secure native randomness when the host lacks it and leaves Expo’s installed fetch unchanged. Import it once at startup; it is a separate entry point so importing the runtime alone does not replace Blob or File. Attachment uploads use Expo’s default fetch; do not opt back into React Native fetch with `EXPO_PUBLIC_USE_RN_FETCH`.
|
|
30
|
+
|
|
31
|
+
Attachment storage requires blobs with `arrayBuffer()`, as provided by Expo Blob and Expo File. Blobs returned by other native libraries may still use React Native's older Blob implementation; convert those to compatible blobs before passing them to the runtime. Cache reads return `expo-file-system` File objects, which Expo fetch accepts as upload bodies.
|
|
32
|
+
|
|
33
|
+
## Effect lifetime
|
|
34
|
+
|
|
35
|
+
`expoRuntime()` provides `Runtimes`, which lazily acquires each requested partition.
|
|
36
|
+
The lower-level `make()` requires `RuntimePartition` and `Scope`; `layer()` provides
|
|
37
|
+
one `Runtime` from that partition. Both accept the same optional `storage` setting; neither accepts owner/namespace arguments.
|
|
38
|
+
The view or managed server selects the partition and owns its scope. Completion, failure
|
|
39
|
+
or interruption closes persistent SQLite connections and removes NetInfo listeners. There is no runtime
|
|
40
|
+
Promise factory or manual runtime cleanup API; a Promise-based ontology view has
|
|
41
|
+
its own `cleanup()`.
|
|
42
|
+
|
|
43
|
+
## Platform secrets
|
|
44
|
+
|
|
45
|
+
The runtime uses SecureStore without an additional biometric authentication
|
|
46
|
+
requirement, so OAuth refresh can run without prompting. There is no global
|
|
47
|
+
`requireAuthentication` option. Interactive session unlock, per-use verification,
|
|
48
|
+
and separate policies for sensitive properties remain future work. Expo runtimes
|
|
49
|
+
supply an EncryptionKeyring backed by SecureStore and native expo-crypto AES-GCM for the downloaded-value row cache.
|
|
50
|
+
|
|
51
|
+
## Native configuration
|
|
52
|
+
|
|
53
|
+
Add the config plugin when using Expo prebuild/CNG:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"expo": {
|
|
58
|
+
"plugins": ["@party-stack/expo-runtime/plugin"]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The separate CommonJS entry runs in Expo's build process and does not load React
|
|
64
|
+
Native or the runtime. It composes the SDK's SecureStore and SQLite plugins.
|
|
65
|
+
SecureStore supplies Android backup exclusions for entries whose device keys
|
|
66
|
+
cannot be restored. SQLite's native configuration stays at SDK defaults.
|
|
67
|
+
No Face ID permission description is added, and an existing application-provided
|
|
68
|
+
description is preserved. WebBrowser's default plugin adds no configuration, so
|
|
69
|
+
it is not included; the experimental Android launcher activity stays disabled.
|
|
70
|
+
Your application still supplies its OAuth redirect scheme/URL.
|
|
71
|
+
|
|
72
|
+
If you maintain custom Android backup rules, include equivalent SecureStore
|
|
73
|
+
exclusions yourself, then configure:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
["@party-stack/expo-runtime/plugin", { "configureAndroidBackup": false }]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The SDK preserves existing custom backup-rule references and warns when they need
|
|
80
|
+
manual integration. Avoid declaring the same SDK plugins separately with competing
|
|
81
|
+
options; Expo runs each plugin once. Changing plugins requires a native rebuild.
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BlobBytesService, ByteInput } from "@party-stack/runtime";
|
|
2
|
+
import { Effect } from "effect";
|
|
2
3
|
//#region src/ExpoFileSystemBlobBytesStore.d.ts
|
|
3
4
|
export interface ExpoFileSystemBlobBytesStoreOptions {
|
|
4
5
|
directoryName: string;
|
|
5
6
|
}
|
|
6
|
-
export declare class ExpoFileSystemBlobBytesStore implements
|
|
7
|
+
export declare class ExpoFileSystemBlobBytesStore implements BlobBytesService {
|
|
7
8
|
#private;
|
|
9
|
+
readonly write: (id: string, value: ByteInput) => Effect.Effect<void, unknown, never>;
|
|
10
|
+
readonly read: (id: string) => Effect.Effect<import("@party-stack/runtime").ByteSource | undefined, import("effect/Cause").UnknownError, never>;
|
|
11
|
+
readonly delete: (id: string) => Effect.Effect<void, import("effect/Cause").UnknownError, never>;
|
|
12
|
+
readonly clear: () => Effect.Effect<void, import("effect/Cause").UnknownError, never>;
|
|
8
13
|
constructor(options: ExpoFileSystemBlobBytesStoreOptions);
|
|
9
|
-
write(id: string, blob: Blob): Promise<void>;
|
|
10
|
-
read(id: string): Promise<Blob>;
|
|
11
|
-
delete(id: string): Promise<void>;
|
|
12
|
-
clear(): Promise<void>;
|
|
13
14
|
}
|
|
14
15
|
//#endregion
|
|
15
16
|
//# sourceMappingURL=ExpoFileSystemBlobBytesStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoFileSystemBlobBytesStore.d.ts","names":[],"sources":["../src/ExpoFileSystemBlobBytesStore.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoFileSystemBlobBytesStore.d.ts","names":[],"sources":["../src/ExpoFileSystemBlobBytesStore.ts"],"mappings":";;;iBAKiB;EACb;;qBAGS,wCAAwC;;WAGxC,QAAK,YAAc,OAAS,cAAS,OAAA;WACrC,OAAI,eAAc,OAAA,sCAAA,+CAAA;WAIlB,SAAM,eAAc,OAAA,oCAAA;WACpB,aAAK,OAAA,oCAAA;EAEd,YAAY,SAAS"}
|
|
@@ -1,35 +1,44 @@
|
|
|
1
|
+
import { hashStorageName } from "./hashStorageName.js";
|
|
2
|
+
import { byteIO, byteSource } from "@party-stack/runtime";
|
|
3
|
+
import { Effect, Stream } from "effect";
|
|
1
4
|
import { Directory, File, Paths } from "expo-file-system";
|
|
2
5
|
//#region src/ExpoFileSystemBlobBytesStore.ts
|
|
3
|
-
function encodeFileName(id) {
|
|
4
|
-
return encodeURIComponent(id);
|
|
5
|
-
}
|
|
6
6
|
var ExpoFileSystemBlobBytesStore = class {
|
|
7
7
|
#directoryName;
|
|
8
|
+
write = (id, value) => byteIO((signal) => this.#write(id, value, signal));
|
|
9
|
+
read = (id) => Effect.tryPromise(() => this.#read(id)).pipe(Effect.map((value) => value === void 0 ? void 0 : byteSource(value)));
|
|
10
|
+
delete = (id) => Effect.tryPromise(() => this.#delete(id));
|
|
11
|
+
clear = () => Effect.tryPromise(() => this.#clear());
|
|
8
12
|
constructor(options) {
|
|
9
13
|
this.#directoryName = options.directoryName;
|
|
10
14
|
}
|
|
11
|
-
async write(id,
|
|
12
|
-
const file = this.#file(id);
|
|
15
|
+
async #write(id, input, signal) {
|
|
16
|
+
const file = await this.#file(id);
|
|
13
17
|
file.create({
|
|
14
18
|
intermediates: true,
|
|
15
19
|
overwrite: true
|
|
16
20
|
});
|
|
17
|
-
|
|
21
|
+
try {
|
|
22
|
+
await Stream.toReadableStream(byteSource(input).stream).pipeTo(file.writableStream(), { signal });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (file.exists) file.delete();
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
|
-
read(id) {
|
|
20
|
-
return Promise.resolve().then(() => {
|
|
21
|
-
const file = this.#file(id);
|
|
22
|
-
if (!file.exists)
|
|
28
|
+
#read(id) {
|
|
29
|
+
return Promise.resolve().then(async () => {
|
|
30
|
+
const file = await this.#file(id);
|
|
31
|
+
if (!file.exists) return;
|
|
23
32
|
return file;
|
|
24
33
|
});
|
|
25
34
|
}
|
|
26
|
-
delete(id) {
|
|
27
|
-
return Promise.resolve().then(() => {
|
|
28
|
-
const file = this.#file(id);
|
|
35
|
+
#delete(id) {
|
|
36
|
+
return Promise.resolve().then(async () => {
|
|
37
|
+
const file = await this.#file(id);
|
|
29
38
|
if (file.exists) file.delete();
|
|
30
39
|
});
|
|
31
40
|
}
|
|
32
|
-
clear() {
|
|
41
|
+
#clear() {
|
|
33
42
|
return Promise.resolve().then(() => {
|
|
34
43
|
const directory = new Directory(Paths.document, this.#directoryName);
|
|
35
44
|
if (directory.exists) directory.delete();
|
|
@@ -44,7 +53,7 @@ var ExpoFileSystemBlobBytesStore = class {
|
|
|
44
53
|
return directory;
|
|
45
54
|
}
|
|
46
55
|
#file(id) {
|
|
47
|
-
return new File(this.#directory(),
|
|
56
|
+
return hashStorageName(id).then((name) => new File(this.#directory(), name));
|
|
48
57
|
}
|
|
49
58
|
};
|
|
50
59
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoFileSystemBlobBytesStore.js","names":[],"sources":["../src/ExpoFileSystemBlobBytesStore.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\nimport
|
|
1
|
+
{"version":3,"file":"ExpoFileSystemBlobBytesStore.js","names":[],"sources":["../src/ExpoFileSystemBlobBytesStore.ts"],"sourcesContent":["import { byteSource, type ByteInput, byteIO, type BlobBytesService } from \"@party-stack/runtime\";\nimport { Effect, Stream } from \"effect\";\nimport { Directory, File, Paths } from \"expo-file-system\";\nimport { hashStorageName } from \"./hashStorageName.js\";\n\nexport interface ExpoFileSystemBlobBytesStoreOptions {\n directoryName: string;\n}\n\nexport class ExpoFileSystemBlobBytesStore implements BlobBytesService {\n #directoryName: string;\n\n readonly write = (id: string, value: ByteInput) => byteIO((signal) => this.#write(id, value, signal));\n readonly read = (id: string) =>\n Effect.tryPromise(() => this.#read(id)).pipe(\n Effect.map((value) => (value === undefined ? undefined : byteSource(value)))\n );\n readonly delete = (id: string) => Effect.tryPromise(() => this.#delete(id));\n readonly clear = () => Effect.tryPromise(() => this.#clear());\n\n constructor(options: ExpoFileSystemBlobBytesStoreOptions) {\n this.#directoryName = options.directoryName;\n }\n\n async #write(id: string, input: ByteInput, signal?: AbortSignal): Promise<void> {\n const file = await this.#file(id);\n file.create({ intermediates: true, overwrite: true });\n try {\n await Stream.toReadableStream(byteSource(input).stream).pipeTo(file.writableStream(), { signal });\n } catch (error) {\n if (file.exists) file.delete();\n throw error;\n }\n }\n\n #read(id: string): Promise<Blob | undefined> {\n return Promise.resolve().then(async () => {\n const file = await this.#file(id);\n if (!file.exists) {\n return undefined;\n }\n return file;\n });\n }\n\n #delete(id: string): Promise<void> {\n return Promise.resolve().then(async () => {\n const file = await this.#file(id);\n if (file.exists) file.delete();\n });\n }\n\n #clear(): Promise<void> {\n return Promise.resolve().then(() => {\n const directory = new Directory(Paths.document, this.#directoryName);\n if (directory.exists) directory.delete();\n });\n }\n\n #directory(): Directory {\n const directory = new Directory(Paths.document, this.#directoryName);\n directory.create({ idempotent: true, intermediates: true });\n return directory;\n }\n\n #file(id: string): Promise<File> {\n return hashStorageName(id).then((name) => new File(this.#directory(), name));\n }\n}\n"],"mappings":";;;;;AASA,IAAa,+BAAb,MAAsE;CAClE;CAEA,SAAkB,IAAY,UAAqB,QAAQ,WAAW,KAAK,OAAO,IAAI,OAAO,MAAM,CAAC;CACpG,QAAiB,OACb,OAAO,iBAAiB,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,KACpC,OAAO,KAAK,UAAW,UAAU,KAAA,IAAY,KAAA,IAAY,WAAW,KAAK,CAAE,CAC/E;CACJ,UAAmB,OAAe,OAAO,iBAAiB,KAAK,QAAQ,EAAE,CAAC;CAC1E,cAAuB,OAAO,iBAAiB,KAAK,OAAO,CAAC;CAE5D,YAAY,SAA8C;EACtD,KAAK,iBAAiB,QAAQ;CAClC;CAEA,MAAM,OAAO,IAAY,OAAkB,QAAqC;EAC5E,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;EAChC,KAAK,OAAO;GAAE,eAAe;GAAM,WAAW;EAAK,CAAC;EACpD,IAAI;GACA,MAAM,OAAO,iBAAiB,WAAW,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,KAAK,eAAe,GAAG,EAAE,OAAO,CAAC;EACpG,SAAS,OAAO;GACZ,IAAI,KAAK,QAAQ,KAAK,OAAO;GAC7B,MAAM;EACV;CACJ;CAEA,MAAM,IAAuC;EACzC,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,YAAY;GACtC,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;GAChC,IAAI,CAAC,KAAK,QACN;GAEJ,OAAO;EACX,CAAC;CACL;CAEA,QAAQ,IAA2B;EAC/B,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,YAAY;GACtC,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;GAChC,IAAI,KAAK,QAAQ,KAAK,OAAO;EACjC,CAAC;CACL;CAEA,SAAwB;EACpB,OAAO,QAAQ,QAAQ,CAAC,CAAC,WAAW;GAChC,MAAM,YAAY,IAAI,UAAU,MAAM,UAAU,KAAK,cAAc;GACnE,IAAI,UAAU,QAAQ,UAAU,OAAO;EAC3C,CAAC;CACL;CAEA,aAAwB;EACpB,MAAM,YAAY,IAAI,UAAU,MAAM,UAAU,KAAK,cAAc;EACnE,UAAU,OAAO;GAAE,YAAY;GAAM,eAAe;EAAK,CAAC;EAC1D,OAAO;CACX;CAEA,MAAM,IAA2B;EAC7B,OAAO,gBAAgB,EAAE,CAAC,CAAC,MAAM,SAAS,IAAI,KAAK,KAAK,WAAW,GAAG,IAAI,CAAC;CAC/E;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoKeyMaterialStore.d.ts","names":[],"sources":["../src/ExpoKeyMaterialStore.ts"],"mappings":";;wBAUgB,qBAAqB,iBAAiB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RuntimeError } from "@party-stack/runtime";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { deleteItemAsync, getItemAsync, setItemAsync } from "expo-secure-store";
|
|
4
|
+
//#region src/ExpoKeyMaterialStore.ts
|
|
5
|
+
function encodeKey(value) {
|
|
6
|
+
const bytes = new TextEncoder().encode(value);
|
|
7
|
+
return `party-stack.${Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
8
|
+
}
|
|
9
|
+
function makeKeyMaterialStore(prefix) {
|
|
10
|
+
const key = (value) => encodeKey(JSON.stringify([prefix, value]));
|
|
11
|
+
const operation = (run) => Effect.tryPromise({
|
|
12
|
+
try: run,
|
|
13
|
+
catch: () => new RuntimeError({
|
|
14
|
+
operation: "secrets",
|
|
15
|
+
cause: /* @__PURE__ */ new Error("SecureStore operation failed.")
|
|
16
|
+
})
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
get: (id) => operation(() => getItemAsync(key(id))).pipe(Effect.map((value) => value ?? void 0)),
|
|
20
|
+
set: (id, value) => operation(() => setItemAsync(key(id), value)),
|
|
21
|
+
delete: (id) => operation(() => deleteItemAsync(key(id)))
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { makeKeyMaterialStore };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=ExpoKeyMaterialStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoKeyMaterialStore.js","names":[],"sources":["../src/ExpoKeyMaterialStore.ts"],"sourcesContent":["import { RuntimeError, type KeyMaterialStore } from \"@party-stack/runtime\";\nimport { Effect } from \"effect\";\nimport { deleteItemAsync, getItemAsync, setItemAsync } from \"expo-secure-store\";\n\n// https://docs.expo.dev/versions/latest/sdk/securestore#securestoresetitemkey-value-options\nfunction encodeKey(value: string): string {\n const bytes = new TextEncoder().encode(value);\n return `party-stack.${Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nexport function makeKeyMaterialStore(prefix: string): KeyMaterialStore {\n const key = (value: string) => encodeKey(JSON.stringify([prefix, value]));\n const operation = <A>(run: () => Promise<A>) =>\n Effect.tryPromise({\n try: run,\n catch: () =>\n new RuntimeError({ operation: \"secrets\", cause: new Error(\"SecureStore operation failed.\") }),\n });\n return {\n get: (id) => operation(() => getItemAsync(key(id))).pipe(Effect.map((value) => value ?? undefined)),\n set: (id, value) => operation(() => setItemAsync(key(id), value)),\n delete: (id) => operation(() => deleteItemAsync(key(id))),\n };\n}\n"],"mappings":";;;;AAKA,SAAS,UAAU,OAAuB;CACtC,MAAM,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;CAC5C,OAAO,eAAe,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AACjG;AAEA,SAAgB,qBAAqB,QAAkC;CACnE,MAAM,OAAO,UAAkB,UAAU,KAAK,UAAU,CAAC,QAAQ,KAAK,CAAC,CAAC;CACxE,MAAM,aAAgB,QAClB,OAAO,WAAW;EACd,KAAK;EACL,aACI,IAAI,aAAa;GAAE,WAAW;GAAW,uBAAO,IAAI,MAAM,+BAA+B;EAAE,CAAC;CACpG,CAAC;CACL,OAAO;EACH,MAAM,OAAO,gBAAgB,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,KAAK,UAAU,SAAS,KAAA,CAAS,CAAC;EAClG,MAAM,IAAI,UAAU,gBAAgB,aAAa,IAAI,EAAE,GAAG,KAAK,CAAC;EAChE,SAAS,OAAO,gBAAgB,gBAAgB,IAAI,EAAE,CAAC,CAAC;CAC5D;AACJ"}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RuntimeError } from "@party-stack/runtime";
|
|
2
|
+
import { Effect } from "effect";
|
|
2
3
|
//#region src/ExpoNetworkConnectivity.d.ts
|
|
3
|
-
export declare
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
static create(): Promise<ExpoNetworkConnectivity>;
|
|
8
|
-
private constructor();
|
|
9
|
-
get isConnected(): boolean;
|
|
10
|
-
subscribe(listener: (isConnected: boolean) => void): () => void;
|
|
11
|
-
close(): void;
|
|
12
|
-
private update;
|
|
13
|
-
}
|
|
4
|
+
export declare const makeExpoNetworkConnectivity: () => Effect.Effect<{
|
|
5
|
+
online: Effect.Effect<boolean, never, never>;
|
|
6
|
+
changes: import("effect/Stream").Stream<boolean, never, never>;
|
|
7
|
+
}, RuntimeError, import("effect/Scope").Scope>;
|
|
14
8
|
//#endregion
|
|
15
9
|
//# sourceMappingURL=ExpoNetworkConnectivity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNetworkConnectivity.d.ts","names":[],"sources":["../src/ExpoNetworkConnectivity.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoNetworkConnectivity.d.ts","names":[],"sources":["../src/ExpoNetworkConnectivity.ts"],"mappings":";;;qBAIa,mCAA2B,OAAA;;;GAetC,qCAAA"}
|
|
@@ -1,39 +1,25 @@
|
|
|
1
|
+
import { RuntimeError, makeNetworkConnectivity } from "@party-stack/runtime";
|
|
2
|
+
import { Effect } from "effect";
|
|
1
3
|
import NetInfo from "@react-native-community/netinfo";
|
|
2
4
|
//#region src/ExpoNetworkConnectivity.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
subscribe(listener) {
|
|
21
|
-
this.listeners.add(listener);
|
|
22
|
-
return () => {
|
|
23
|
-
this.listeners.delete(listener);
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
close() {
|
|
27
|
-
this.unsubscribe();
|
|
28
|
-
this.listeners.clear();
|
|
29
|
-
}
|
|
30
|
-
update(isConnected) {
|
|
31
|
-
if (isConnected === this.connected) return;
|
|
32
|
-
this.connected = isConnected;
|
|
33
|
-
for (const listener of this.listeners) listener(isConnected);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
5
|
+
const makeExpoNetworkConnectivity = Effect.fnUntraced(function* () {
|
|
6
|
+
const initial = yield* Effect.tryPromise({
|
|
7
|
+
try: () => NetInfo.fetch(),
|
|
8
|
+
catch: (cause) => new RuntimeError({
|
|
9
|
+
operation: "expoRuntime.connectivity",
|
|
10
|
+
cause
|
|
11
|
+
})
|
|
12
|
+
});
|
|
13
|
+
const state = yield* makeNetworkConnectivity(initial.isConnected === true);
|
|
14
|
+
yield* Effect.acquireRelease(Effect.sync(() => NetInfo.addEventListener((value) => {
|
|
15
|
+
Effect.runSync(state.set(value.isConnected === true));
|
|
16
|
+
})), (unsubscribe) => Effect.sync(unsubscribe));
|
|
17
|
+
return {
|
|
18
|
+
online: state.online,
|
|
19
|
+
changes: state.changes
|
|
20
|
+
};
|
|
21
|
+
});
|
|
36
22
|
//#endregion
|
|
37
|
-
export {
|
|
23
|
+
export { makeExpoNetworkConnectivity };
|
|
38
24
|
|
|
39
25
|
//# sourceMappingURL=ExpoNetworkConnectivity.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNetworkConnectivity.js","names":[],"sources":["../src/ExpoNetworkConnectivity.ts"],"sourcesContent":["import NetInfo from \"@react-native-community/netinfo\";\nimport
|
|
1
|
+
{"version":3,"file":"ExpoNetworkConnectivity.js","names":[],"sources":["../src/ExpoNetworkConnectivity.ts"],"sourcesContent":["import { makeNetworkConnectivity, RuntimeError } from \"@party-stack/runtime\";\nimport NetInfo from \"@react-native-community/netinfo\";\nimport { Effect } from \"effect\";\n\nexport const makeExpoNetworkConnectivity = Effect.fnUntraced(function* () {\n const initial = yield* Effect.tryPromise({\n try: () => NetInfo.fetch(),\n catch: (cause) => new RuntimeError({ operation: \"expoRuntime.connectivity\", cause }),\n });\n const state = yield* makeNetworkConnectivity(initial.isConnected === true);\n yield* Effect.acquireRelease(\n Effect.sync(() =>\n NetInfo.addEventListener((value) => {\n Effect.runSync(state.set(value.isConnected === true));\n })\n ),\n (unsubscribe) => Effect.sync(unsubscribe)\n );\n return { online: state.online, changes: state.changes };\n});\n"],"mappings":";;;;AAIA,MAAa,8BAA8B,OAAO,WAAW,aAAa;CACtE,MAAM,UAAU,OAAO,OAAO,WAAW;EACrC,WAAW,QAAQ,MAAM;EACzB,QAAQ,UAAU,IAAI,aAAa;GAAE,WAAW;GAA4B;EAAM,CAAC;CACvF,CAAC;CACD,MAAM,QAAQ,OAAO,wBAAwB,QAAQ,gBAAgB,IAAI;CACzE,OAAO,OAAO,eACV,OAAO,WACH,QAAQ,kBAAkB,UAAU;EAChC,OAAO,QAAQ,MAAM,IAAI,MAAM,gBAAgB,IAAI,CAAC;CACxD,CAAC,CACL,IACC,gBAAgB,OAAO,KAAK,WAAW,CAC5C;CACA,OAAO;EAAE,QAAQ,MAAM;EAAQ,SAAS,MAAM;CAAQ;AAC1D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExpoBrowserAuthentication.d.ts","names":[],"sources":["../src/createExpoBrowserAuthentication.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"createExpoBrowserAuthentication.d.ts","names":[],"sources":["../src/createExpoBrowserAuthentication.ts"],"mappings":";;wBAIgB,mCAAmC"}
|
|
@@ -1,19 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BrowserAuthenticationError } from "@party-stack/runtime";
|
|
2
|
+
import { Effect } from "effect";
|
|
2
3
|
import { dismissAuthSession, openAuthSessionAsync } from "expo-web-browser";
|
|
3
4
|
//#region src/createExpoBrowserAuthentication.ts
|
|
4
5
|
function createExpoBrowserAuthentication() {
|
|
5
|
-
return { start({ redirectUrl }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
close() {
|
|
13
|
-
dismissAuthSession();
|
|
14
|
-
}
|
|
6
|
+
return { start: Effect.fnUntraced(function* ({ redirectUrl }) {
|
|
7
|
+
let used = false;
|
|
8
|
+
let closed = false;
|
|
9
|
+
const dismiss = () => {
|
|
10
|
+
if (closed) return;
|
|
11
|
+
closed = true;
|
|
12
|
+
if (used) dismissAuthSession();
|
|
15
13
|
};
|
|
16
|
-
|
|
14
|
+
yield* Effect.addFinalizer(() => Effect.sync(dismiss));
|
|
15
|
+
return { open: (authorizationUrl) => Effect.gen(function* () {
|
|
16
|
+
if (used || closed) return yield* new BrowserAuthenticationError({
|
|
17
|
+
reason: "Configuration",
|
|
18
|
+
message: "This authentication session has already been used or closed."
|
|
19
|
+
});
|
|
20
|
+
used = true;
|
|
21
|
+
const result = yield* Effect.tryPromise({
|
|
22
|
+
try: () => openAuthSessionAsync(authorizationUrl, redirectUrl),
|
|
23
|
+
catch: (cause) => new BrowserAuthenticationError({
|
|
24
|
+
reason: "Platform",
|
|
25
|
+
message: "Could not open browser authorization.",
|
|
26
|
+
cause
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
if (result.type === "success") return { callbackUrl: result.url };
|
|
30
|
+
return yield* new BrowserAuthenticationError({
|
|
31
|
+
reason: "Cancelled",
|
|
32
|
+
message: "Browser authentication was cancelled."
|
|
33
|
+
});
|
|
34
|
+
}).pipe(Effect.onInterrupt(() => Effect.sync(dismiss))) };
|
|
35
|
+
}) };
|
|
17
36
|
}
|
|
18
37
|
//#endregion
|
|
19
38
|
export { createExpoBrowserAuthentication };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExpoBrowserAuthentication.js","names":[],"sources":["../src/createExpoBrowserAuthentication.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"createExpoBrowserAuthentication.js","names":[],"sources":["../src/createExpoBrowserAuthentication.ts"],"sourcesContent":["import { BrowserAuthenticationError, type BrowserAuthentication } from \"@party-stack/runtime\";\nimport { Effect } from \"effect\";\nimport { dismissAuthSession, openAuthSessionAsync } from \"expo-web-browser\";\n\nexport function createExpoBrowserAuthentication(): BrowserAuthentication {\n return {\n start: Effect.fnUntraced(function* ({ redirectUrl }) {\n let used = false;\n let closed = false;\n const dismiss = () => {\n if (closed) return;\n closed = true;\n if (used) dismissAuthSession();\n };\n yield* Effect.addFinalizer(() => Effect.sync(dismiss));\n return {\n open: (authorizationUrl: string) =>\n Effect.gen(function* () {\n if (used || closed)\n return yield* new BrowserAuthenticationError({\n reason: \"Configuration\",\n message: \"This authentication session has already been used or closed.\",\n });\n used = true;\n const result = yield* Effect.tryPromise({\n try: () => openAuthSessionAsync(authorizationUrl, redirectUrl),\n catch: (cause) =>\n new BrowserAuthenticationError({\n reason: \"Platform\",\n message: \"Could not open browser authorization.\",\n cause,\n }),\n });\n if (result.type === \"success\") return { callbackUrl: result.url };\n return yield* new BrowserAuthenticationError({\n reason: \"Cancelled\",\n message: \"Browser authentication was cancelled.\",\n });\n }).pipe(Effect.onInterrupt(() => Effect.sync(dismiss))),\n };\n }),\n };\n}\n"],"mappings":";;;;AAIA,SAAgB,kCAAyD;CACrE,OAAO,EACH,OAAO,OAAO,WAAW,WAAW,EAAE,eAAe;EACjD,IAAI,OAAO;EACX,IAAI,SAAS;EACb,MAAM,gBAAgB;GAClB,IAAI,QAAQ;GACZ,SAAS;GACT,IAAI,MAAM,mBAAmB;EACjC;EACA,OAAO,OAAO,mBAAmB,OAAO,KAAK,OAAO,CAAC;EACrD,OAAO,EACH,OAAO,qBACH,OAAO,IAAI,aAAa;GACpB,IAAI,QAAQ,QACR,OAAO,OAAO,IAAI,2BAA2B;IACzC,QAAQ;IACR,SAAS;GACb,CAAC;GACL,OAAO;GACP,MAAM,SAAS,OAAO,OAAO,WAAW;IACpC,WAAW,qBAAqB,kBAAkB,WAAW;IAC7D,QAAQ,UACJ,IAAI,2BAA2B;KAC3B,QAAQ;KACR,SAAS;KACT;IACJ,CAAC;GACT,CAAC;GACD,IAAI,OAAO,SAAS,WAAW,OAAO,EAAE,aAAa,OAAO,IAAI;GAChE,OAAO,OAAO,IAAI,2BAA2B;IACzC,QAAQ;IACR,SAAS;GACb,CAAC;EACL,CAAC,CAAC,CAAC,KAAK,OAAO,kBAAkB,OAAO,KAAK,OAAO,CAAC,CAAC,EAC9D;CACJ,CAAC,EACL;AACJ"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "
|
|
1
|
+
import { Runtime, RuntimeError, RuntimePartition, Runtimes } from "@party-stack/runtime";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
3
|
//#region src/createExpoRuntime.d.ts
|
|
4
|
-
export
|
|
4
|
+
export interface ExpoRuntimeOptions {
|
|
5
|
+
/** Persistent SQLite/filesystem storage by default; memory keeps native platform capabilities. */
|
|
6
|
+
readonly storage?: "persistent" | "memory";
|
|
7
|
+
}
|
|
8
|
+
export declare const make: (options?: ExpoRuntimeOptions | undefined) => Effect.Effect<import("@party-stack/runtime").RuntimeService, import("@party-stack/ontology-storage").OntologyStorageError | RuntimeError, RuntimePartition | import("effect/Scope").Scope>;
|
|
9
|
+
export declare const layer: (options?: ExpoRuntimeOptions) => Layer.Layer<Runtime, import("@party-stack/ontology-storage").OntologyStorageError | RuntimeError, RuntimePartition>;
|
|
10
|
+
export declare const expoRuntime: (options?: ExpoRuntimeOptions) => Layer.Layer<Runtimes, never, never>;
|
|
5
11
|
//#endregion
|
|
6
12
|
//# sourceMappingURL=createExpoRuntime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExpoRuntime.d.ts","names":[],"sources":["../src/createExpoRuntime.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"createExpoRuntime.d.ts","names":[],"sources":["../src/createExpoRuntime.ts"],"mappings":";;;iBAmBiB;;WAEJ;;qBAGA,OAAI,UAAA,mCAAA,OAAA,sCAAA,wDAAA,uBAAA,cAAA,0CAAA;qBAuGJ,QAAK,UAAa,uBAAkB,MAAA,MAAA,iDAAA,uBAAA,cAAA;qBACpC,cAAW,UAAa,uBAAkB,MAAA,MAAA"}
|
package/lib/createExpoRuntime.js
CHANGED
|
@@ -1,39 +1,119 @@
|
|
|
1
|
+
import { hashStorageName } from "./hashStorageName.js";
|
|
1
2
|
import { ExpoFileSystemBlobBytesStore } from "./ExpoFileSystemBlobBytesStore.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { makeKeyMaterialStore } from "./ExpoKeyMaterialStore.js";
|
|
4
|
+
import { makeExpoNetworkConnectivity } from "./ExpoNetworkConnectivity.js";
|
|
4
5
|
import { createExpoBrowserAuthentication } from "./createExpoBrowserAuthentication.js";
|
|
5
|
-
import {
|
|
6
|
-
import "
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { nativeKeyCodec } from "./encryptionKeys.js";
|
|
7
|
+
import { MemoryBlobBytesStore, Runtime, RuntimeError, RuntimePartition, Runtimes, makeKeyMaterialStoreKeyring } from "@party-stack/runtime";
|
|
8
|
+
import { Effect, Layer, Schema } from "effect";
|
|
9
|
+
import { Paths } from "expo-file-system";
|
|
10
|
+
import { storageError } from "@party-stack/ontology-storage/driver";
|
|
11
|
+
import * as MemoryTables from "@party-stack/ontology-storage/memory/tables";
|
|
12
|
+
import * as SQLite from "@party-stack/sql-ontology-storage/sqlite";
|
|
13
|
+
import * as SqlTables from "@party-stack/sql-ontology-storage/tables";
|
|
14
|
+
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
15
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
9
16
|
import { deleteDatabaseAsync, openDatabaseAsync } from "expo-sqlite";
|
|
10
17
|
//#region src/createExpoRuntime.ts
|
|
11
|
-
const
|
|
18
|
+
const make = Effect.fn("ExpoRuntime.make")(function* (options = {}) {
|
|
19
|
+
const { owner, namespace } = yield* RuntimePartition;
|
|
20
|
+
const connectivity = yield* makeExpoNetworkConnectivity();
|
|
21
|
+
if (options.storage === "memory") {
|
|
22
|
+
const materials = /* @__PURE__ */ new Map();
|
|
23
|
+
return yield* Runtime.make({
|
|
24
|
+
tables: yield* MemoryTables.make(),
|
|
25
|
+
blobBytes: new MemoryBlobBytesStore(),
|
|
26
|
+
encryptionKeyring: yield* makeKeyMaterialStoreKeyring({
|
|
27
|
+
get: (id) => Effect.sync(() => materials.get(id)),
|
|
28
|
+
set: (id, value) => Effect.sync(() => {
|
|
29
|
+
materials.set(id, value);
|
|
30
|
+
}),
|
|
31
|
+
delete: (id) => Effect.sync(() => {
|
|
32
|
+
materials.delete(id);
|
|
33
|
+
})
|
|
34
|
+
}, nativeKeyCodec),
|
|
35
|
+
browserAuthentication: createExpoBrowserAuthentication(),
|
|
36
|
+
connectivity,
|
|
37
|
+
capacity: {
|
|
38
|
+
memory: Effect.succeed(void 0),
|
|
39
|
+
storage: Effect.succeed(void 0)
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
12
43
|
const name = `party-stack:${owner}:${namespace}`;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
const storageName = `party-stack-${yield* Effect.tryPromise({
|
|
45
|
+
try: () => hashStorageName(JSON.stringify([owner, namespace])),
|
|
46
|
+
catch: (cause) => new RuntimeError({
|
|
47
|
+
operation: "expoRuntime.name",
|
|
48
|
+
cause
|
|
49
|
+
})
|
|
50
|
+
})}`;
|
|
51
|
+
const blobBytes = new ExpoFileSystemBlobBytesStore({ directoryName: `${storageName}-blobs` });
|
|
52
|
+
const tables = Effect.gen(function* () {
|
|
53
|
+
const database = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
54
|
+
try: () => openDatabaseAsync(`${storageName}-storage.db`),
|
|
55
|
+
catch: (cause) => storageError("sql", "open", cause)
|
|
56
|
+
}), (database) => Effect.promise(() => database.closeAsync()));
|
|
57
|
+
let active = database;
|
|
58
|
+
const sql = yield* SQLite.makeClient({
|
|
59
|
+
exec: (query) => active.execSync(query),
|
|
60
|
+
prepare: (query) => ({
|
|
61
|
+
all: (...values) => active.getAllSync(query, values),
|
|
62
|
+
run: (...values) => active.runSync(query, values)
|
|
63
|
+
}),
|
|
64
|
+
transactionAsync: (body) => {
|
|
65
|
+
let value;
|
|
66
|
+
return database.withExclusiveTransactionAsync(async (tx) => {
|
|
67
|
+
active = tx;
|
|
68
|
+
try {
|
|
69
|
+
value = await body();
|
|
70
|
+
} finally {
|
|
71
|
+
active = database;
|
|
72
|
+
}
|
|
73
|
+
}).then(() => value);
|
|
74
|
+
}
|
|
75
|
+
}).pipe(Effect.provide(Reactivity.layer), Effect.mapError((cause) => storageError("sql", "open", cause)));
|
|
76
|
+
return yield* SqlTables.make().pipe(Effect.provideService(SqlClient.SqlClient, sql));
|
|
77
|
+
});
|
|
78
|
+
const secrets = makeKeyMaterialStore(`${name}:secrets`);
|
|
79
|
+
const ownedTables = yield* tables;
|
|
80
|
+
const encryptionKeyring = yield* makeKeyMaterialStoreKeyring(secrets, nativeKeyCodec);
|
|
81
|
+
return yield* Runtime.make({
|
|
20
82
|
blobBytes,
|
|
83
|
+
capacity: {
|
|
84
|
+
memory: Effect.succeed(void 0),
|
|
85
|
+
storage: Effect.try({
|
|
86
|
+
try: () => ({
|
|
87
|
+
scope: "filesystem",
|
|
88
|
+
limitBytes: Paths.totalDiskSpace,
|
|
89
|
+
availableBytes: Paths.availableDiskSpace
|
|
90
|
+
}),
|
|
91
|
+
catch: (cause) => new RuntimeError({
|
|
92
|
+
operation: "capacity.storage",
|
|
93
|
+
cause
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
},
|
|
21
97
|
browserAuthentication: createExpoBrowserAuthentication(),
|
|
22
|
-
|
|
98
|
+
encryptionKeyring,
|
|
23
99
|
connectivity,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
100
|
+
tables: ownedTables,
|
|
101
|
+
destroy: Effect.tryPromise({
|
|
102
|
+
try: async () => {
|
|
103
|
+
await Promise.all([deleteDatabaseAsync(`${storageName}-storage.db`).catch((cause) => {
|
|
104
|
+
if (!Schema.is(Schema.Struct({ code: Schema.Literal("ERR_DATABASE_NOT_FOUND") }))(cause)) throw cause;
|
|
105
|
+
}), Effect.runPromise(blobBytes.clear())]);
|
|
106
|
+
},
|
|
107
|
+
catch: (cause) => new RuntimeError({
|
|
108
|
+
operation: "destroy",
|
|
109
|
+
cause
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
});
|
|
35
113
|
});
|
|
114
|
+
const layer = (options = {}) => Layer.effect(Runtime, make(options));
|
|
115
|
+
const expoRuntime = (options = {}) => Runtimes.layer(layer(options), { browserAuthentication: createExpoBrowserAuthentication() });
|
|
36
116
|
//#endregion
|
|
37
|
-
export {
|
|
117
|
+
export { expoRuntime, layer, make };
|
|
38
118
|
|
|
39
119
|
//# sourceMappingURL=createExpoRuntime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExpoRuntime.js","names":[],"sources":["../src/createExpoRuntime.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"createExpoRuntime.js","names":[],"sources":["../src/createExpoRuntime.ts"],"sourcesContent":["import { storageError } from \"@party-stack/ontology-storage/driver\";\nimport * as MemoryTables from \"@party-stack/ontology-storage/memory/tables\";\nimport { Runtime, RuntimeError, RuntimePartition, makeKeyMaterialStoreKeyring } from \"@party-stack/runtime\";\nimport { Runtimes, MemoryBlobBytesStore, type KeyMaterialStore } from \"@party-stack/runtime\";\nimport * as SQLite from \"@party-stack/sql-ontology-storage/sqlite\";\nimport * as SqlTables from \"@party-stack/sql-ontology-storage/tables\";\nimport { Effect, Layer, Schema } from \"effect\";\nimport * as Reactivity from \"effect/reactivity/Reactivity\";\nimport * as SqlClient from \"effect/sql/SqlClient\";\nimport { Paths } from \"expo-file-system\";\nimport { deleteDatabaseAsync, openDatabaseAsync } from \"expo-sqlite\";\nimport { createExpoBrowserAuthentication } from \"./createExpoBrowserAuthentication.js\";\nimport { nativeKeyCodec } from \"./encryptionKeys.js\";\nimport { ExpoFileSystemBlobBytesStore } from \"./ExpoFileSystemBlobBytesStore.js\";\nimport { makeKeyMaterialStore } from \"./ExpoKeyMaterialStore.js\";\nimport { makeExpoNetworkConnectivity } from \"./ExpoNetworkConnectivity.js\";\nimport { hashStorageName } from \"./hashStorageName.js\";\nimport type { SQLiteBindValue } from \"expo-sqlite\";\n\nexport interface ExpoRuntimeOptions {\n /** Persistent SQLite/filesystem storage by default; memory keeps native platform capabilities. */\n readonly storage?: \"persistent\" | \"memory\";\n}\n\nexport const make = Effect.fn(\"ExpoRuntime.make\")(function* (options: ExpoRuntimeOptions = {}) {\n const { owner, namespace } = yield* RuntimePartition;\n const connectivity = yield* makeExpoNetworkConnectivity();\n if (options.storage === \"memory\") {\n const materials = new Map<string, string>();\n const secrets: KeyMaterialStore = {\n get: (id) => Effect.sync(() => materials.get(id)),\n set: (id, value) =>\n Effect.sync(() => {\n materials.set(id, value);\n }),\n delete: (id) =>\n Effect.sync(() => {\n materials.delete(id);\n }),\n };\n return yield* Runtime.make({\n tables: yield* MemoryTables.make(),\n blobBytes: new MemoryBlobBytesStore(),\n encryptionKeyring: yield* makeKeyMaterialStoreKeyring(secrets, nativeKeyCodec),\n browserAuthentication: createExpoBrowserAuthentication(),\n connectivity,\n capacity: { memory: Effect.succeed(undefined), storage: Effect.succeed(undefined) },\n });\n }\n const name = `party-stack:${owner}:${namespace}`;\n const storageName = `party-stack-${yield* Effect.tryPromise({ try: () => hashStorageName(JSON.stringify([owner, namespace])), catch: (cause) => new RuntimeError({ operation: \"expoRuntime.name\", cause }) })}`;\n const blobBytes = new ExpoFileSystemBlobBytesStore({\n directoryName: `${storageName}-blobs`,\n });\n const tables = Effect.gen(function* () {\n const database = yield* Effect.acquireRelease(\n Effect.tryPromise({\n try: () => openDatabaseAsync(`${storageName}-storage.db`),\n catch: (cause) => storageError(\"sql\", \"open\", cause),\n }),\n (database) => Effect.promise(() => database.closeAsync())\n );\n let active = database;\n const sql = yield* SQLite.makeClient({\n exec: (query) => active.execSync(query),\n prepare: (query) => ({\n all: (...values) => active.getAllSync(query, values as SQLiteBindValue[]),\n run: (...values) => active.runSync(query, values as SQLiteBindValue[]),\n }),\n transactionAsync: <A>(body: () => Promise<A>) => {\n let value: A;\n return database\n .withExclusiveTransactionAsync(async (tx) => {\n active = tx;\n try {\n value = await body();\n } finally {\n active = database;\n }\n })\n .then(() => value);\n },\n }).pipe(\n Effect.provide(Reactivity.layer),\n Effect.mapError((cause) => storageError(\"sql\", \"open\", cause))\n );\n return yield* SqlTables.make().pipe(Effect.provideService(SqlClient.SqlClient, sql));\n });\n const secrets = makeKeyMaterialStore(`${name}:secrets`);\n const ownedTables = yield* tables;\n const encryptionKeyring = yield* makeKeyMaterialStoreKeyring(secrets, nativeKeyCodec);\n return yield* Runtime.make({\n blobBytes,\n capacity: {\n memory: Effect.succeed(undefined),\n storage: Effect.try({\n try: () => ({\n scope: \"filesystem\" as const,\n limitBytes: Paths.totalDiskSpace,\n availableBytes: Paths.availableDiskSpace,\n }),\n catch: (cause) => new RuntimeError({ operation: \"capacity.storage\", cause }),\n }),\n },\n browserAuthentication: createExpoBrowserAuthentication(),\n encryptionKeyring,\n connectivity,\n\n tables: ownedTables,\n destroy: Effect.tryPromise({\n try: async () => {\n await Promise.all([\n deleteDatabaseAsync(`${storageName}-storage.db`).catch((cause) => {\n if (\n !Schema.is(Schema.Struct({ code: Schema.Literal(\"ERR_DATABASE_NOT_FOUND\") }))(\n cause\n )\n )\n throw cause;\n }),\n Effect.runPromise(blobBytes.clear()),\n ]);\n },\n catch: (cause) => new RuntimeError({ operation: \"destroy\", cause }),\n }),\n });\n});\nexport const layer = (options: ExpoRuntimeOptions = {}) => Layer.effect(Runtime, make(options));\nexport const expoRuntime = (options: ExpoRuntimeOptions = {}) =>\n Runtimes.layer(layer(options), { browserAuthentication: createExpoBrowserAuthentication() });\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,MAAa,OAAO,OAAO,GAAG,kBAAkB,CAAC,CAAC,WAAW,UAA8B,CAAC,GAAG;CAC3F,MAAM,EAAE,OAAO,cAAc,OAAO;CACpC,MAAM,eAAe,OAAO,4BAA4B;CACxD,IAAI,QAAQ,YAAY,UAAU;EAC9B,MAAM,4BAAY,IAAI,IAAoB;EAY1C,OAAO,OAAO,QAAQ,KAAK;GACvB,QAAQ,OAAO,aAAa,KAAK;GACjC,WAAW,IAAI,qBAAqB;GACpC,mBAAmB,OAAO,4BAA4B;IAbtD,MAAM,OAAO,OAAO,WAAW,UAAU,IAAI,EAAE,CAAC;IAChD,MAAM,IAAI,UACN,OAAO,WAAW;KACd,UAAU,IAAI,IAAI,KAAK;IAC3B,CAAC;IACL,SAAS,OACL,OAAO,WAAW;KACd,UAAU,OAAO,EAAE;IACvB,CAAC;GAKuD,GAAG,cAAc;GAC7E,uBAAuB,gCAAgC;GACvD;GACA,UAAU;IAAE,QAAQ,OAAO,QAAQ,KAAA,CAAS;IAAG,SAAS,OAAO,QAAQ,KAAA,CAAS;GAAE;EACtF,CAAC;CACL;CACA,MAAM,OAAO,eAAe,MAAM,GAAG;CACrC,MAAM,cAAc,eAAe,OAAO,OAAO,WAAW;EAAE,WAAW,gBAAgB,KAAK,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;EAAG,QAAQ,UAAU,IAAI,aAAa;GAAE,WAAW;GAAoB;EAAM,CAAC;CAAE,CAAC;CAC5M,MAAM,YAAY,IAAI,6BAA6B,EAC/C,eAAe,GAAG,YAAY,QAClC,CAAC;CACD,MAAM,SAAS,OAAO,IAAI,aAAa;EACnC,MAAM,WAAW,OAAO,OAAO,eAC3B,OAAO,WAAW;GACd,WAAW,kBAAkB,GAAG,YAAY,YAAY;GACxD,QAAQ,UAAU,aAAa,OAAO,QAAQ,KAAK;EACvD,CAAC,IACA,aAAa,OAAO,cAAc,SAAS,WAAW,CAAC,CAC5D;EACA,IAAI,SAAS;EACb,MAAM,MAAM,OAAO,OAAO,WAAW;GACjC,OAAO,UAAU,OAAO,SAAS,KAAK;GACtC,UAAU,WAAW;IACjB,MAAM,GAAG,WAAW,OAAO,WAAW,OAAO,MAA2B;IACxE,MAAM,GAAG,WAAW,OAAO,QAAQ,OAAO,MAA2B;GACzE;GACA,mBAAsB,SAA2B;IAC7C,IAAI;IACJ,OAAO,SACF,8BAA8B,OAAO,OAAO;KACzC,SAAS;KACT,IAAI;MACA,QAAQ,MAAM,KAAK;KACvB,UAAU;MACN,SAAS;KACb;IACJ,CAAC,CAAC,CACD,WAAW,KAAK;GACzB;EACJ,CAAC,CAAC,CAAC,KACC,OAAO,QAAQ,WAAW,KAAK,GAC/B,OAAO,UAAU,UAAU,aAAa,OAAO,QAAQ,KAAK,CAAC,CACjE;EACA,OAAO,OAAO,UAAU,KAAK,CAAC,CAAC,KAAK,OAAO,eAAe,UAAU,WAAW,GAAG,CAAC;CACvF,CAAC;CACD,MAAM,UAAU,qBAAqB,GAAG,KAAK,SAAS;CACtD,MAAM,cAAc,OAAO;CAC3B,MAAM,oBAAoB,OAAO,4BAA4B,SAAS,cAAc;CACpF,OAAO,OAAO,QAAQ,KAAK;EACvB;EACA,UAAU;GACN,QAAQ,OAAO,QAAQ,KAAA,CAAS;GAChC,SAAS,OAAO,IAAI;IAChB,YAAY;KACR,OAAO;KACP,YAAY,MAAM;KAClB,gBAAgB,MAAM;IAC1B;IACA,QAAQ,UAAU,IAAI,aAAa;KAAE,WAAW;KAAoB;IAAM,CAAC;GAC/E,CAAC;EACL;EACA,uBAAuB,gCAAgC;EACvD;EACA;EAEA,QAAQ;EACR,SAAS,OAAO,WAAW;GACvB,KAAK,YAAY;IACb,MAAM,QAAQ,IAAI,CACd,oBAAoB,GAAG,YAAY,YAAY,CAAC,CAAC,OAAO,UAAU;KAC9D,IACI,CAAC,OAAO,GAAG,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,wBAAwB,EAAE,CAAC,CAAC,CAAC,CACzE,KACJ,GAEA,MAAM;IACd,CAAC,GACD,OAAO,WAAW,UAAU,MAAM,CAAC,CACvC,CAAC;GACL;GACA,QAAQ,UAAU,IAAI,aAAa;IAAE,WAAW;IAAW;GAAM,CAAC;EACtE,CAAC;CACL,CAAC;AACL,CAAC;AACD,MAAa,SAAS,UAA8B,CAAC,MAAM,MAAM,OAAO,SAAS,KAAK,OAAO,CAAC;AAC9F,MAAa,eAAe,UAA8B,CAAC,MACvD,SAAS,MAAM,MAAM,OAAO,GAAG,EAAE,uBAAuB,gCAAgC,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { KeyMaterialCodec } from "@party-stack/runtime";
|
|
2
|
+
//#region src/encryptionKeys.d.ts
|
|
3
|
+
/** Native AES-GCM uses the same authenticated envelope as the Web Crypto implementation. */
|
|
4
|
+
export declare const nativeKeyCodec: KeyMaterialCodec;
|
|
5
|
+
//#endregion
|
|
6
|
+
//# sourceMappingURL=encryptionKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptionKeys.d.ts","names":[],"sources":["../src/encryptionKeys.ts"],"mappings":";;;qBAIa,gBAAgB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AESEncryptionKey, AESSealedData, aesDecryptAsync, aesEncryptAsync } from "expo-crypto";
|
|
2
|
+
//#region src/encryptionKeys.ts
|
|
3
|
+
/** Native AES-GCM uses the same authenticated envelope as the Web Crypto implementation. */
|
|
4
|
+
const nativeKeyCodec = {
|
|
5
|
+
generate: async () => (await AESEncryptionKey.generate()).encoded("base64"),
|
|
6
|
+
import: async (encoded) => {
|
|
7
|
+
const key = await AESEncryptionKey.import(encoded, "base64");
|
|
8
|
+
return {
|
|
9
|
+
encrypt: async (plaintext, iv, additionalData) => {
|
|
10
|
+
return (await aesEncryptAsync(plaintext, key, {
|
|
11
|
+
nonce: { bytes: iv },
|
|
12
|
+
additionalData
|
|
13
|
+
})).ciphertext({ includeTag: true });
|
|
14
|
+
},
|
|
15
|
+
decrypt: (ciphertext, iv, additionalData) => aesDecryptAsync(AESSealedData.fromParts(iv, ciphertext, 16), key, { additionalData })
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { nativeKeyCodec };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=encryptionKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryptionKeys.js","names":[],"sources":["../src/encryptionKeys.ts"],"sourcesContent":["import { AESEncryptionKey, AESSealedData, aesDecryptAsync, aesEncryptAsync } from \"expo-crypto\";\nimport type { KeyMaterialCodec } from \"@party-stack/runtime\";\n\n/** Native AES-GCM uses the same authenticated envelope as the Web Crypto implementation. */\nexport const nativeKeyCodec: KeyMaterialCodec = {\n generate: async () => (await AESEncryptionKey.generate()).encoded(\"base64\"),\n import: async (encoded) => {\n const key = await AESEncryptionKey.import(encoded, \"base64\");\n return {\n encrypt: async (plaintext, iv, additionalData) => {\n const sealed = await aesEncryptAsync(plaintext, key, {\n nonce: { bytes: iv },\n additionalData,\n });\n return sealed.ciphertext({ includeTag: true });\n },\n decrypt: (ciphertext, iv, additionalData) =>\n aesDecryptAsync(AESSealedData.fromParts(iv, ciphertext, 16), key, { additionalData }),\n };\n },\n};\n"],"mappings":";;;AAIA,MAAa,iBAAmC;CAC5C,UAAU,aAAa,MAAM,iBAAiB,SAAS,EAAA,CAAG,QAAQ,QAAQ;CAC1E,QAAQ,OAAO,YAAY;EACvB,MAAM,MAAM,MAAM,iBAAiB,OAAO,SAAS,QAAQ;EAC3D,OAAO;GACH,SAAS,OAAO,WAAW,IAAI,mBAAmB;IAK9C,QAAO,MAJc,gBAAgB,WAAW,KAAK;KACjD,OAAO,EAAE,OAAO,GAAG;KACnB;IACJ,CAAC,EAAA,CACa,WAAW,EAAE,YAAY,KAAK,CAAC;GACjD;GACA,UAAU,YAAY,IAAI,mBACtB,gBAAgB,cAAc,UAAU,IAAI,YAAY,EAAE,GAAG,KAAK,EAAE,eAAe,CAAC;EAC5F;CACJ;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashStorageName.d.ts","names":[],"sources":["../src/hashStorageName.ts"],"mappings":";wBAEgB,gBAAgB,gBAAgB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CryptoDigestAlgorithm, digestStringAsync } from "expo-crypto";
|
|
2
|
+
//#region src/hashStorageName.ts
|
|
3
|
+
function hashStorageName(value) {
|
|
4
|
+
return digestStringAsync(CryptoDigestAlgorithm.SHA256, value);
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { hashStorageName };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=hashStorageName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashStorageName.js","names":[],"sources":["../src/hashStorageName.ts"],"sourcesContent":["import { CryptoDigestAlgorithm, digestStringAsync } from \"expo-crypto\";\n\nexport function hashStorageName(value: string): Promise<string> {\n return digestStringAsync(CryptoDigestAlgorithm.SHA256, value);\n}\n"],"mappings":";;AAEA,SAAgB,gBAAgB,OAAgC;CAC5D,OAAO,kBAAkB,sBAAsB,QAAQ,KAAK;AAChE"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { ExpoRuntimeOptions, expoRuntime, layer, make } from "./createExpoRuntime.js";
|
|
2
|
+
export { type ExpoRuntimeOptions, expoRuntime, layer, make };
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { createExpoRuntime };
|
|
1
|
+
export { expoRuntime, layer, make } from "./createExpoRuntime.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/lib/polyfill.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getRandomValues, randomUUID } from "expo-crypto";
|
|
2
|
+
import { Blob } from "expo-blob";
|
|
3
|
+
//#region src/polyfill.ts
|
|
4
|
+
if (typeof globalThis.crypto === "undefined") Object.defineProperty(globalThis, "crypto", {
|
|
5
|
+
value: {},
|
|
6
|
+
configurable: true
|
|
7
|
+
});
|
|
8
|
+
if (typeof globalThis.crypto.getRandomValues !== "function") Object.defineProperty(globalThis.crypto, "getRandomValues", {
|
|
9
|
+
value: getRandomValues,
|
|
10
|
+
configurable: true
|
|
11
|
+
});
|
|
12
|
+
if (typeof globalThis.crypto.randomUUID !== "function") Object.defineProperty(globalThis.crypto, "randomUUID", {
|
|
13
|
+
value: randomUUID,
|
|
14
|
+
configurable: true
|
|
15
|
+
});
|
|
16
|
+
globalThis.Blob = Blob;
|
|
17
|
+
var ExpoFile = class extends globalThis.Blob {
|
|
18
|
+
#name;
|
|
19
|
+
#lastModified;
|
|
20
|
+
constructor(parts, name, options = {}) {
|
|
21
|
+
super(parts, options);
|
|
22
|
+
this.#name = String(name);
|
|
23
|
+
this.#lastModified = options.lastModified ?? Date.now();
|
|
24
|
+
Object.defineProperty(this, "name", {
|
|
25
|
+
value: this.#name,
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
get name() {
|
|
30
|
+
return this.#name;
|
|
31
|
+
}
|
|
32
|
+
get lastModified() {
|
|
33
|
+
return this.#lastModified;
|
|
34
|
+
}
|
|
35
|
+
get webkitRelativePath() {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
get [Symbol.toStringTag]() {
|
|
39
|
+
return "File";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
globalThis.File = ExpoFile;
|
|
43
|
+
//#endregion
|
|
44
|
+
export {};
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=polyfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polyfill.js","names":[],"sources":["../src/polyfill.ts"],"sourcesContent":["import { Blob } from \"expo-blob\";\nimport { getRandomValues, randomUUID } from \"expo-crypto\";\n\n// Native AES is provided separately; only install the secure randomness used by portable IDs and nonces.\nif (typeof globalThis.crypto === \"undefined\")\n Object.defineProperty(globalThis, \"crypto\", { value: {}, configurable: true });\nif (typeof globalThis.crypto.getRandomValues !== \"function\")\n Object.defineProperty(globalThis.crypto, \"getRandomValues\", {\n value: getRandomValues,\n configurable: true,\n });\nif (typeof globalThis.crypto.randomUUID !== \"function\")\n Object.defineProperty(globalThis.crypto, \"randomUUID\", { value: randomUUID, configurable: true });\n\n// Expo declarations predate the current DOM Blob byte-array generics.\nglobalThis.Blob = Blob as unknown as typeof globalThis.Blob;\n\n// React Native File extends its own Blob class, so replace it alongside Blob.\nclass ExpoFile extends globalThis.Blob implements File {\n readonly #name: string;\n readonly #lastModified: number;\n\n constructor(parts: BlobPart[], name: string, options: FilePropertyBag = {}) {\n super(parts, options);\n this.#name = String(name);\n this.#lastModified = options.lastModified ?? Date.now();\n // Expo FormData checks own descriptors before assigning a blob filename.\n // An inherited getter alone would make that assignment throw.\n Object.defineProperty(this, \"name\", { value: this.#name, enumerable: true });\n }\n\n get name(): string {\n return this.#name;\n }\n\n get lastModified(): number {\n return this.#lastModified;\n }\n\n get webkitRelativePath(): string {\n return \"\";\n }\n\n get [Symbol.toStringTag](): string {\n return \"File\";\n }\n}\n\nglobalThis.File = ExpoFile;\n"],"mappings":";;;AAIA,IAAI,OAAO,WAAW,WAAW,aAC7B,OAAO,eAAe,YAAY,UAAU;CAAE,OAAO,CAAC;CAAG,cAAc;AAAK,CAAC;AACjF,IAAI,OAAO,WAAW,OAAO,oBAAoB,YAC7C,OAAO,eAAe,WAAW,QAAQ,mBAAmB;CACxD,OAAO;CACP,cAAc;AAClB,CAAC;AACL,IAAI,OAAO,WAAW,OAAO,eAAe,YACxC,OAAO,eAAe,WAAW,QAAQ,cAAc;CAAE,OAAO;CAAY,cAAc;AAAK,CAAC;AAGpG,WAAW,OAAO;AAGlB,IAAM,WAAN,cAAuB,WAAW,KAAqB;CACnD;CACA;CAEA,YAAY,OAAmB,MAAc,UAA2B,CAAC,GAAG;EACxE,MAAM,OAAO,OAAO;EACpB,KAAK,QAAQ,OAAO,IAAI;EACxB,KAAK,gBAAgB,QAAQ,gBAAgB,KAAK,IAAI;EAGtD,OAAO,eAAe,MAAM,QAAQ;GAAE,OAAO,KAAK;GAAO,YAAY;EAAK,CAAC;CAC/E;CAEA,IAAI,OAAe;EACf,OAAO,KAAK;CAChB;CAEA,IAAI,eAAuB;EACvB,OAAO,KAAK;CAChB;CAEA,IAAI,qBAA6B;EAC7B,OAAO;CACX;CAEA,KAAK,OAAO,eAAuB;EAC/B,OAAO;CACX;AACJ;AAEA,WAAW,OAAO"}
|
package/package.json
CHANGED
|
@@ -1,35 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@party-stack/expo-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"files": [
|
|
5
|
-
"lib"
|
|
5
|
+
"lib",
|
|
6
|
+
"plugin"
|
|
6
7
|
],
|
|
7
8
|
"type": "module",
|
|
9
|
+
"sideEffects": [
|
|
10
|
+
"./lib/polyfill.js"
|
|
11
|
+
],
|
|
8
12
|
"exports": {
|
|
9
13
|
".": {
|
|
10
14
|
"types": "./lib/index.d.ts",
|
|
11
15
|
"default": "./lib/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./polyfill": {
|
|
18
|
+
"types": "./lib/polyfill.d.ts",
|
|
19
|
+
"default": "./lib/polyfill.js"
|
|
20
|
+
},
|
|
21
|
+
"./plugin": {
|
|
22
|
+
"types": "./plugin/index.d.cts",
|
|
23
|
+
"default": "./plugin/index.cjs"
|
|
12
24
|
}
|
|
13
25
|
},
|
|
14
26
|
"dependencies": {
|
|
15
|
-
"
|
|
16
|
-
"expo-
|
|
17
|
-
"expo-
|
|
27
|
+
"effect": "^4.0.5",
|
|
28
|
+
"expo-blob": "^57.0.2",
|
|
29
|
+
"expo-crypto": "^57.0.4",
|
|
30
|
+
"expo-secure-store": "^57.0.4",
|
|
31
|
+
"expo-web-browser": "^57.0.3",
|
|
18
32
|
"temporal-polyfill": "^0.3.0",
|
|
19
|
-
"@party-stack/
|
|
20
|
-
"@party-stack/runtime": "0.
|
|
33
|
+
"@party-stack/ontology-storage": "0.2.0",
|
|
34
|
+
"@party-stack/runtime": "0.5.0",
|
|
35
|
+
"@party-stack/sql-ontology-storage": "0.2.0"
|
|
21
36
|
},
|
|
22
37
|
"devDependencies": {
|
|
23
38
|
"@react-native-community/netinfo": "^12.0.1",
|
|
24
|
-
"expo
|
|
25
|
-
"expo-
|
|
39
|
+
"expo": "^57.0.29",
|
|
40
|
+
"expo-file-system": "^57.0.7",
|
|
41
|
+
"expo-sqlite": "^57.0.4",
|
|
26
42
|
"typescript": "~7.0.2",
|
|
27
|
-
"vite
|
|
28
|
-
"
|
|
43
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.2.0",
|
|
44
|
+
"vite-plus": "1.2.0",
|
|
45
|
+
"@party-stack/build-config": "0.2.0"
|
|
29
46
|
},
|
|
30
47
|
"peerDependencies": {
|
|
31
48
|
"@react-native-community/netinfo": "^12.0.1",
|
|
32
|
-
"expo
|
|
33
|
-
"expo-
|
|
49
|
+
"expo": "^57.0.29",
|
|
50
|
+
"expo-file-system": "^57.0.7",
|
|
51
|
+
"expo-sqlite": "^57.0.4"
|
|
34
52
|
}
|
|
35
53
|
}
|
package/plugin/index.cjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { createRunOncePlugin, withPlugins } = require("expo/config-plugins");
|
|
2
|
+
const withSecureStore = require("expo-secure-store/app.plugin.js").default;
|
|
3
|
+
const withSQLite = require("expo-sqlite/app.plugin.js").default;
|
|
4
|
+
const { name, version } = require("../package.json");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {Parameters<import("expo/config-plugins.js").ConfigPlugin>[0]} config
|
|
8
|
+
* @param {{ configureAndroidBackup?: boolean }} [options]
|
|
9
|
+
*/
|
|
10
|
+
const withPartyStack = (config, options = {}) =>
|
|
11
|
+
withPlugins(config, [
|
|
12
|
+
[
|
|
13
|
+
withSecureStore,
|
|
14
|
+
{
|
|
15
|
+
configureAndroidBackup: options.configureAndroidBackup ?? true,
|
|
16
|
+
faceIDPermission: config.ios?.infoPlist?.NSFaceIDUsageDescription ?? false,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
[withSQLite, {}],
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
module.exports = createRunOncePlugin(withPartyStack, name, version);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConfigPlugin } from "expo/config-plugins.js";
|
|
2
|
+
interface PartyStackPluginOptions {
|
|
3
|
+
/** Keep SecureStore values out of Android backups. Defaults to true.
|
|
4
|
+
* Disable only when supplying equivalent custom backup rules.
|
|
5
|
+
*/
|
|
6
|
+
readonly configureAndroidBackup?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const withPartyStack: ConfigPlugin<PartyStackPluginOptions | void>;
|
|
9
|
+
export = withPartyStack;
|
package/lib/ExpoSecretStore.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { SecretStore } from "@party-stack/runtime";
|
|
2
|
-
//#region src/ExpoSecretStore.d.ts
|
|
3
|
-
export declare class ExpoSecretStore implements SecretStore {
|
|
4
|
-
private readonly prefix;
|
|
5
|
-
constructor(prefix: string);
|
|
6
|
-
get(key: string): Promise<string | undefined>;
|
|
7
|
-
set(key: string, value: string): Promise<void>;
|
|
8
|
-
delete(key: string): Promise<void>;
|
|
9
|
-
private key;
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
|
-
//# sourceMappingURL=ExpoSecretStore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSecretStore.d.ts","names":[],"sources":["../src/ExpoSecretStore.ts"],"mappings":";;qBASa,2BAA2B;mBACP;EAA7B,YAA6B;EAE7B,IAAI,cAAc;EAIlB,IAAI,aAAa,gBAAgB;EAIjC,OAAO,cAAc;UAIb"}
|
package/lib/ExpoSecretStore.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { deleteItemAsync, getItemAsync, setItemAsync } from "expo-secure-store";
|
|
2
|
-
//#region src/ExpoSecretStore.ts
|
|
3
|
-
function encodeKey(value) {
|
|
4
|
-
const bytes = new TextEncoder().encode(value);
|
|
5
|
-
return `party-stack.${Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
6
|
-
}
|
|
7
|
-
var ExpoSecretStore = class {
|
|
8
|
-
prefix;
|
|
9
|
-
constructor(prefix) {
|
|
10
|
-
this.prefix = prefix;
|
|
11
|
-
}
|
|
12
|
-
get(key) {
|
|
13
|
-
return getItemAsync(this.key(key)).then((value) => value ?? void 0);
|
|
14
|
-
}
|
|
15
|
-
set(key, value) {
|
|
16
|
-
return setItemAsync(this.key(key), value);
|
|
17
|
-
}
|
|
18
|
-
delete(key) {
|
|
19
|
-
return deleteItemAsync(this.key(key));
|
|
20
|
-
}
|
|
21
|
-
key(key) {
|
|
22
|
-
return encodeKey(JSON.stringify([this.prefix, key]));
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
//#endregion
|
|
26
|
-
export { ExpoSecretStore };
|
|
27
|
-
|
|
28
|
-
//# sourceMappingURL=ExpoSecretStore.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSecretStore.js","names":[],"sources":["../src/ExpoSecretStore.ts"],"sourcesContent":["import { deleteItemAsync, getItemAsync, setItemAsync } from \"expo-secure-store\";\nimport type { SecretStore } from \"@party-stack/runtime\";\n\n// https://docs.expo.dev/versions/latest/sdk/securestore#securestoresetitemkey-value-options\nfunction encodeKey(value: string): string {\n const bytes = new TextEncoder().encode(value);\n return `party-stack.${Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nexport class ExpoSecretStore implements SecretStore {\n constructor(private readonly prefix: string) {}\n\n get(key: string): Promise<string | undefined> {\n return getItemAsync(this.key(key)).then((value) => value ?? undefined);\n }\n\n set(key: string, value: string): Promise<void> {\n return setItemAsync(this.key(key), value);\n }\n\n delete(key: string): Promise<void> {\n return deleteItemAsync(this.key(key));\n }\n\n private key(key: string): string {\n return encodeKey(JSON.stringify([this.prefix, key]));\n }\n}\n"],"mappings":";;AAIA,SAAS,UAAU,OAAuB;CACtC,MAAM,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;CAC5C,OAAO,eAAe,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AACjG;AAEA,IAAa,kBAAb,MAAoD;CACnB;CAA7B,YAAY,QAAiC;EAAhB,KAAA,SAAA;CAAiB;CAE9C,IAAI,KAA0C;EAC1C,OAAO,aAAa,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,UAAU,SAAS,KAAA,CAAS;CACzE;CAEA,IAAI,KAAa,OAA8B;EAC3C,OAAO,aAAa,KAAK,IAAI,GAAG,GAAG,KAAK;CAC5C;CAEA,OAAO,KAA4B;EAC/B,OAAO,gBAAgB,KAAK,IAAI,GAAG,CAAC;CACxC;CAEA,IAAY,KAAqB;EAC7B,OAAO,UAAU,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,CAAC,CAAC;CACvD;AACJ"}
|