@party-stack/expo-runtime 0.4.2 → 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.
Files changed (35) hide show
  1. package/README.md +68 -3
  2. package/lib/ExpoFileSystemBlobBytesStore.d.ts +7 -6
  3. package/lib/ExpoFileSystemBlobBytesStore.d.ts.map +1 -1
  4. package/lib/ExpoFileSystemBlobBytesStore.js +17 -7
  5. package/lib/ExpoFileSystemBlobBytesStore.js.map +1 -1
  6. package/lib/ExpoKeyMaterialStore.d.ts +5 -0
  7. package/lib/ExpoKeyMaterialStore.d.ts.map +1 -0
  8. package/lib/ExpoKeyMaterialStore.js +27 -0
  9. package/lib/ExpoKeyMaterialStore.js.map +1 -0
  10. package/lib/ExpoNetworkConnectivity.d.ts +6 -12
  11. package/lib/ExpoNetworkConnectivity.d.ts.map +1 -1
  12. package/lib/ExpoNetworkConnectivity.js +20 -34
  13. package/lib/ExpoNetworkConnectivity.js.map +1 -1
  14. package/lib/createExpoBrowserAuthentication.d.ts.map +1 -1
  15. package/lib/createExpoBrowserAuthentication.js +31 -12
  16. package/lib/createExpoBrowserAuthentication.js.map +1 -1
  17. package/lib/createExpoRuntime.d.ts +9 -3
  18. package/lib/createExpoRuntime.d.ts.map +1 -1
  19. package/lib/createExpoRuntime.js +105 -27
  20. package/lib/createExpoRuntime.js.map +1 -1
  21. package/lib/encryptionKeys.d.ts +6 -0
  22. package/lib/encryptionKeys.d.ts.map +1 -0
  23. package/lib/encryptionKeys.js +22 -0
  24. package/lib/encryptionKeys.js.map +1 -0
  25. package/lib/index.d.ts +2 -2
  26. package/lib/index.js +1 -1
  27. package/lib/polyfill.js +13 -0
  28. package/lib/polyfill.js.map +1 -1
  29. package/package.json +13 -6
  30. package/plugin/index.cjs +22 -0
  31. package/plugin/index.d.cts +9 -0
  32. package/lib/ExpoSecretStore.d.ts +0 -12
  33. package/lib/ExpoSecretStore.d.ts.map +0 -1
  34. package/lib/ExpoSecretStore.js +0 -28
  35. package/lib/ExpoSecretStore.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Expo runtime
2
2
 
3
- This runtime targets Expo SDK 57. Install `@party-stack/expo-runtime` and its Expo peer dependencies using SDK 57-compatible versions. Expo installs `expo/fetch` as the default native fetch starting in SDK 56; no separate `expo-fetch` package or fetch installer is needed. The runtime includes `expo-blob`.
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
4
 
5
5
  Import the required polyfill first in your application entry point, before importing application code or creating clients:
6
6
 
@@ -9,8 +9,73 @@ import "@party-stack/expo-runtime/polyfill";
9
9
  import "expo-router/entry";
10
10
  ```
11
11
 
12
- Then use `createExpoRuntime` from `@party-stack/expo-runtime` as your installation's runtime provider.
12
+ Use the lazy `expoRuntime()` layer from `@party-stack/expo-runtime` for native persistence:
13
13
 
14
- The polyfill replaces global `Blob` with Expo Blob and global `File` with a compatible class extending Expo Blob. It 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`.
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`.
15
30
 
16
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 { BlobBytesStore } from "@party-stack/runtime";
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 BlobBytesStore {
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":";;iBAIiB;EACb;;qBAGS,wCAAwC;;EAGjD,YAAY,SAAS;EAIf,MAAM,YAAY,MAAM,OAAO;EAOrC,KAAK,aAAa,QAAQ;EAU1B,OAAO,aAAa;EAOpB,SAAS"}
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,34 +1,44 @@
1
1
  import { hashStorageName } from "./hashStorageName.js";
2
+ import { byteIO, byteSource } from "@party-stack/runtime";
3
+ import { Effect, Stream } from "effect";
2
4
  import { Directory, File, Paths } from "expo-file-system";
3
5
  //#region src/ExpoFileSystemBlobBytesStore.ts
4
6
  var ExpoFileSystemBlobBytesStore = class {
5
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());
6
12
  constructor(options) {
7
13
  this.#directoryName = options.directoryName;
8
14
  }
9
- async write(id, blob) {
15
+ async #write(id, input, signal) {
10
16
  const file = await this.#file(id);
11
- const bytes = new Uint8Array(await blob.arrayBuffer());
12
17
  file.create({
13
18
  intermediates: true,
14
19
  overwrite: true
15
20
  });
16
- file.write(bytes);
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
+ }
17
27
  }
18
- read(id) {
28
+ #read(id) {
19
29
  return Promise.resolve().then(async () => {
20
30
  const file = await this.#file(id);
21
- if (!file.exists) throw new Error(`Blob bytes not found for "${id}".`);
31
+ if (!file.exists) return;
22
32
  return file;
23
33
  });
24
34
  }
25
- delete(id) {
35
+ #delete(id) {
26
36
  return Promise.resolve().then(async () => {
27
37
  const file = await this.#file(id);
28
38
  if (file.exists) file.delete();
29
39
  });
30
40
  }
31
- clear() {
41
+ #clear() {
32
42
  return Promise.resolve().then(() => {
33
43
  const directory = new Directory(Paths.document, this.#directoryName);
34
44
  if (directory.exists) directory.delete();
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoFileSystemBlobBytesStore.js","names":[],"sources":["../src/ExpoFileSystemBlobBytesStore.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\nimport { hashStorageName } from \"./hashStorageName.js\";\nimport type { BlobBytesStore } from \"@party-stack/runtime\";\n\nexport interface ExpoFileSystemBlobBytesStoreOptions {\n directoryName: string;\n}\n\nexport class ExpoFileSystemBlobBytesStore implements BlobBytesStore {\n #directoryName: string;\n\n constructor(options: ExpoFileSystemBlobBytesStoreOptions) {\n this.#directoryName = options.directoryName;\n }\n\n async write(id: string, blob: Blob): Promise<void> {\n const file = await this.#file(id);\n const bytes = new Uint8Array(await blob.arrayBuffer());\n file.create({ intermediates: true, overwrite: true });\n file.write(bytes);\n }\n\n read(id: string): Promise<Blob> {\n return Promise.resolve().then(async () => {\n const file = await this.#file(id);\n if (!file.exists) {\n throw new Error(`Blob bytes not found for \"${id}\".`);\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":";;;AAQA,IAAa,+BAAb,MAAoE;CAChE;CAEA,YAAY,SAA8C;EACtD,KAAK,iBAAiB,QAAQ;CAClC;CAEA,MAAM,MAAM,IAAY,MAA2B;EAC/C,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;EAChC,MAAM,QAAQ,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;EACrD,KAAK,OAAO;GAAE,eAAe;GAAM,WAAW;EAAK,CAAC;EACpD,KAAK,MAAM,KAAK;CACpB;CAEA,KAAK,IAA2B;EAC5B,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,YAAY;GACtC,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;GAChC,IAAI,CAAC,KAAK,QACN,MAAM,IAAI,MAAM,6BAA6B,GAAG,GAAG;GAEvD,OAAO;EACX,CAAC;CACL;CAEA,OAAO,IAA2B;EAC9B,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,YAAY;GACtC,MAAM,OAAO,MAAM,KAAK,MAAM,EAAE;GAChC,IAAI,KAAK,QAAQ,KAAK,OAAO;EACjC,CAAC;CACL;CAEA,QAAuB;EACnB,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"}
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,5 @@
1
+ import { KeyMaterialStore } from "@party-stack/runtime";
2
+ //#region src/ExpoKeyMaterialStore.d.ts
3
+ export declare function makeKeyMaterialStore(prefix: string): KeyMaterialStore;
4
+ //#endregion
5
+ //# sourceMappingURL=ExpoKeyMaterialStore.d.ts.map
@@ -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 { NetworkConnectivity } from "@party-stack/runtime";
1
+ import { RuntimeError } from "@party-stack/runtime";
2
+ import { Effect } from "effect";
2
3
  //#region src/ExpoNetworkConnectivity.d.ts
3
- export declare class ExpoNetworkConnectivity implements NetworkConnectivity {
4
- private readonly listeners;
5
- private connected;
6
- private readonly unsubscribe;
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":";;qBAGa,mCAAmC;mBAC3B;UACT;mBACS;SAEJ,UAAU,QAAQ;UAKxB;MAOH;EAIJ,UAAU,WAAW;EAOrB;UAKQ"}
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
- var ExpoNetworkConnectivity = class ExpoNetworkConnectivity {
4
- listeners = /* @__PURE__ */ new Set();
5
- connected;
6
- unsubscribe;
7
- static async create() {
8
- const initialState = await NetInfo.fetch();
9
- return new ExpoNetworkConnectivity(initialState.isConnected === true);
10
- }
11
- constructor(initialIsConnected) {
12
- this.connected = initialIsConnected;
13
- this.unsubscribe = NetInfo.addEventListener((state) => {
14
- this.update(state.isConnected === true);
15
- });
16
- }
17
- get isConnected() {
18
- return this.connected;
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 { ExpoNetworkConnectivity };
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 type { NetworkConnectivity } from \"@party-stack/runtime\";\n\nexport class ExpoNetworkConnectivity implements NetworkConnectivity {\n private readonly listeners = new Set<(isConnected: boolean) => void>();\n private connected: boolean;\n private readonly unsubscribe: () => void;\n\n static async create(): Promise<ExpoNetworkConnectivity> {\n const initialState = await NetInfo.fetch();\n return new ExpoNetworkConnectivity(initialState.isConnected === true);\n }\n\n private constructor(initialIsConnected: boolean) {\n this.connected = initialIsConnected;\n this.unsubscribe = NetInfo.addEventListener((state) => {\n this.update(state.isConnected === true);\n });\n }\n\n get isConnected(): boolean {\n return this.connected;\n }\n\n subscribe(listener: (isConnected: boolean) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n close(): void {\n this.unsubscribe();\n this.listeners.clear();\n }\n\n private update(isConnected: boolean): void {\n if (isConnected === this.connected) return;\n this.connected = isConnected;\n for (const listener of this.listeners) {\n listener(isConnected);\n }\n }\n}\n"],"mappings":";;AAGA,IAAa,0BAAb,MAAa,wBAAuD;CAChE,4BAA6B,IAAI,IAAoC;CACrE;CACA;CAEA,aAAa,SAA2C;EACpD,MAAM,eAAe,MAAM,QAAQ,MAAM;EACzC,OAAO,IAAI,wBAAwB,aAAa,gBAAgB,IAAI;CACxE;CAEA,YAAoB,oBAA6B;EAC7C,KAAK,YAAY;EACjB,KAAK,cAAc,QAAQ,kBAAkB,UAAU;GACnD,KAAK,OAAO,MAAM,gBAAgB,IAAI;EAC1C,CAAC;CACL;CAEA,IAAI,cAAuB;EACvB,OAAO,KAAK;CAChB;CAEA,UAAU,UAAsD;EAC5D,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACT,KAAK,UAAU,OAAO,QAAQ;EAClC;CACJ;CAEA,QAAc;EACV,KAAK,YAAY;EACjB,KAAK,UAAU,MAAM;CACzB;CAEA,OAAe,aAA4B;EACvC,IAAI,gBAAgB,KAAK,WAAW;EACpC,KAAK,YAAY;EACjB,KAAK,MAAM,YAAY,KAAK,WACxB,SAAS,WAAW;CAE5B;AACJ"}
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":";;wBAGgB,mCAAmC"}
1
+ {"version":3,"file":"createExpoBrowserAuthentication.d.ts","names":[],"sources":["../src/createExpoBrowserAuthentication.ts"],"mappings":";;wBAIgB,mCAAmC"}
@@ -1,19 +1,38 @@
1
- import { BrowserAuthenticationCancelledError } from "@party-stack/runtime";
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
- return {
7
- async open(authorizationUrl) {
8
- const result = await openAuthSessionAsync(authorizationUrl, redirectUrl);
9
- if (result.type === "success") return { callbackUrl: result.url };
10
- throw new BrowserAuthenticationCancelledError();
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 { BrowserAuthenticationCancelledError, type BrowserAuthentication } from \"@party-stack/runtime\";\nimport { dismissAuthSession, openAuthSessionAsync } from \"expo-web-browser\";\n\nexport function createExpoBrowserAuthentication(): BrowserAuthentication {\n return {\n start({ redirectUrl }) {\n return {\n async open(authorizationUrl) {\n const result = await openAuthSessionAsync(authorizationUrl, redirectUrl);\n if (result.type === \"success\") {\n return {\n callbackUrl: result.url,\n };\n }\n throw new BrowserAuthenticationCancelledError();\n },\n close() {\n dismissAuthSession();\n },\n };\n },\n };\n}\n"],"mappings":";;;AAGA,SAAgB,kCAAyD;CACrE,OAAO,EACH,MAAM,EAAE,eAAe;EACnB,OAAO;GACH,MAAM,KAAK,kBAAkB;IACzB,MAAM,SAAS,MAAM,qBAAqB,kBAAkB,WAAW;IACvE,IAAI,OAAO,SAAS,WAChB,OAAO,EACH,aAAa,OAAO,IACxB;IAEJ,MAAM,IAAI,oCAAoC;GAClD;GACA,QAAQ;IACJ,mBAAmB;GACvB;EACJ;CACJ,EACJ;AACJ"}
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 { RuntimeAdapter } from "@party-stack/runtime";
2
- import "temporal-polyfill/global";
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 declare const createExpoRuntime: (owner: string, namespace: string) => Promise<RuntimeAdapter>;
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":";;;qBAaa,oBAAoB,eAAe,sBAAsB,QAAQ"}
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"}
@@ -1,41 +1,119 @@
1
1
  import { hashStorageName } from "./hashStorageName.js";
2
2
  import { ExpoFileSystemBlobBytesStore } from "./ExpoFileSystemBlobBytesStore.js";
3
- import { ExpoNetworkConnectivity } from "./ExpoNetworkConnectivity.js";
4
- import { ExpoSecretStore } from "./ExpoSecretStore.js";
3
+ import { makeKeyMaterialStore } from "./ExpoKeyMaterialStore.js";
4
+ import { makeExpoNetworkConnectivity } from "./ExpoNetworkConnectivity.js";
5
5
  import { createExpoBrowserAuthentication } from "./createExpoBrowserAuthentication.js";
6
- import { defineRuntime } from "@party-stack/runtime";
7
- import "temporal-polyfill/global";
8
- import { SingleProcessCoordination } from "@party-stack/coordination";
9
- import { createExpoSQLitePersistence } from "@tanstack/expo-db-sqlite-persistence";
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";
10
16
  import { deleteDatabaseAsync, openDatabaseAsync } from "expo-sqlite";
11
17
  //#region src/createExpoRuntime.ts
12
- const createExpoRuntime = defineRuntime(async (owner, namespace) => {
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
+ }
13
43
  const name = `party-stack:${owner}:${namespace}`;
14
- const storageName = `party-stack-${await hashStorageName(JSON.stringify([owner, namespace]))}`;
15
- const [database, connectivity] = await Promise.all([openDatabaseAsync(`${storageName}.db`), ExpoNetworkConnectivity.create()]);
16
- const persistence = createExpoSQLitePersistence({ database });
17
- const coordination = new SingleProcessCoordination({ scope: name });
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
+ })}`;
18
51
  const blobBytes = new ExpoFileSystemBlobBytesStore({ directoryName: `${storageName}-blobs` });
19
- return {
20
- owner,
21
- namespace,
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({
22
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
+ },
23
97
  browserAuthentication: createExpoBrowserAuthentication(),
24
- secrets: new ExpoSecretStore(`${name}:secrets`),
98
+ encryptionKeyring,
25
99
  connectivity,
26
- coordination,
27
- persistence,
28
- destroy: async () => {
29
- await Promise.all([deleteDatabaseAsync(`${storageName}.db`), blobBytes.clear()]);
30
- },
31
- cleanup: async () => {
32
- await coordination.close();
33
- connectivity.close();
34
- await database.closeAsync();
35
- }
36
- };
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
+ });
37
113
  });
114
+ const layer = (options = {}) => Layer.effect(Runtime, make(options));
115
+ const expoRuntime = (options = {}) => Runtimes.layer(layer(options), { browserAuthentication: createExpoBrowserAuthentication() });
38
116
  //#endregion
39
- export { createExpoRuntime };
117
+ export { expoRuntime, layer, make };
40
118
 
41
119
  //# sourceMappingURL=createExpoRuntime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createExpoRuntime.js","names":[],"sources":["../src/createExpoRuntime.ts"],"sourcesContent":["// SQLite persistence revives Temporal values through the registered global constructors.\nimport \"temporal-polyfill/global\";\nimport { SingleProcessCoordination } from \"@party-stack/coordination\";\nimport { defineRuntime } from \"@party-stack/runtime\";\nimport { createExpoSQLitePersistence } from \"@tanstack/expo-db-sqlite-persistence\";\nimport { deleteDatabaseAsync, openDatabaseAsync } from \"expo-sqlite\";\nimport { createExpoBrowserAuthentication } from \"./createExpoBrowserAuthentication.js\";\nimport { ExpoFileSystemBlobBytesStore } from \"./ExpoFileSystemBlobBytesStore.js\";\nimport { ExpoNetworkConnectivity } from \"./ExpoNetworkConnectivity.js\";\nimport { ExpoSecretStore } from \"./ExpoSecretStore.js\";\nimport { hashStorageName } from \"./hashStorageName.js\";\nimport type { RuntimeAdapter } from \"@party-stack/runtime\";\n\nexport const createExpoRuntime: (owner: string, namespace: string) => Promise<RuntimeAdapter> = defineRuntime(\n async (owner, namespace) => {\n const name = `party-stack:${owner}:${namespace}`;\n const storageName = `party-stack-${await hashStorageName(JSON.stringify([owner, namespace]))}`;\n const [database, connectivity] = await Promise.all([\n openDatabaseAsync(`${storageName}.db`),\n ExpoNetworkConnectivity.create(),\n ]);\n const persistence = createExpoSQLitePersistence({\n database: database,\n });\n const coordination = new SingleProcessCoordination({\n scope: name,\n });\n const blobBytes = new ExpoFileSystemBlobBytesStore({\n directoryName: `${storageName}-blobs`,\n });\n return {\n owner,\n namespace,\n blobBytes,\n browserAuthentication: createExpoBrowserAuthentication(),\n secrets: new ExpoSecretStore(`${name}:secrets`),\n connectivity,\n coordination,\n persistence,\n destroy: async () => {\n await Promise.all([deleteDatabaseAsync(`${storageName}.db`), blobBytes.clear()]);\n },\n cleanup: async () => {\n await coordination.close();\n connectivity.close();\n await database.closeAsync();\n },\n };\n }\n);\n"],"mappings":";;;;;;;;;;;AAaA,MAAa,oBAAmF,cAC5F,OAAO,OAAO,cAAc;CACxB,MAAM,OAAO,eAAe,MAAM,GAAG;CACrC,MAAM,cAAc,eAAe,MAAM,gBAAgB,KAAK,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;CAC3F,MAAM,CAAC,UAAU,gBAAgB,MAAM,QAAQ,IAAI,CAC/C,kBAAkB,GAAG,YAAY,IAAI,GACrC,wBAAwB,OAAO,CACnC,CAAC;CACD,MAAM,cAAc,4BAA4B,EAClC,SACd,CAAC;CACD,MAAM,eAAe,IAAI,0BAA0B,EAC/C,OAAO,KACX,CAAC;CACD,MAAM,YAAY,IAAI,6BAA6B,EAC/C,eAAe,GAAG,YAAY,QAClC,CAAC;CACD,OAAO;EACH;EACA;EACA;EACA,uBAAuB,gCAAgC;EACvD,SAAS,IAAI,gBAAgB,GAAG,KAAK,SAAS;EAC9C;EACA;EACA;EACA,SAAS,YAAY;GACjB,MAAM,QAAQ,IAAI,CAAC,oBAAoB,GAAG,YAAY,IAAI,GAAG,UAAU,MAAM,CAAC,CAAC;EACnF;EACA,SAAS,YAAY;GACjB,MAAM,aAAa,MAAM;GACzB,aAAa,MAAM;GACnB,MAAM,SAAS,WAAW;EAC9B;CACJ;AACJ,CACJ"}
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"}
package/lib/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { createExpoRuntime } from "./createExpoRuntime.js";
2
- export { createExpoRuntime };
1
+ import { ExpoRuntimeOptions, expoRuntime, layer, make } from "./createExpoRuntime.js";
2
+ export { type ExpoRuntimeOptions, expoRuntime, layer, make };
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- export { createExpoRuntime } from "./createExpoRuntime.js";
1
+ export { expoRuntime, layer, make } from "./createExpoRuntime.js";
package/lib/polyfill.js CHANGED
@@ -1,5 +1,18 @@
1
+ import { getRandomValues, randomUUID } from "expo-crypto";
1
2
  import { Blob } from "expo-blob";
2
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
+ });
3
16
  globalThis.Blob = Blob;
4
17
  var ExpoFile = class extends globalThis.Blob {
5
18
  #name;
@@ -1 +1 @@
1
- {"version":3,"file":"polyfill.js","names":[],"sources":["../src/polyfill.ts"],"sourcesContent":["import { Blob } from \"expo-blob\";\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":";;AAGA,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"}
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,8 +1,9 @@
1
1
  {
2
2
  "name": "@party-stack/expo-runtime",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "files": [
5
- "lib"
5
+ "lib",
6
+ "plugin"
6
7
  ],
7
8
  "type": "module",
8
9
  "sideEffects": [
@@ -16,17 +17,22 @@
16
17
  "./polyfill": {
17
18
  "types": "./lib/polyfill.d.ts",
18
19
  "default": "./lib/polyfill.js"
20
+ },
21
+ "./plugin": {
22
+ "types": "./plugin/index.d.cts",
23
+ "default": "./plugin/index.cjs"
19
24
  }
20
25
  },
21
26
  "dependencies": {
22
- "@tanstack/expo-db-sqlite-persistence": "0.2.34",
27
+ "effect": "^4.0.5",
23
28
  "expo-blob": "^57.0.2",
24
29
  "expo-crypto": "^57.0.4",
25
30
  "expo-secure-store": "^57.0.4",
26
31
  "expo-web-browser": "^57.0.3",
27
32
  "temporal-polyfill": "^0.3.0",
28
- "@party-stack/coordination": "0.3.1",
29
- "@party-stack/runtime": "0.4.2"
33
+ "@party-stack/ontology-storage": "0.2.0",
34
+ "@party-stack/runtime": "0.5.0",
35
+ "@party-stack/sql-ontology-storage": "0.2.0"
30
36
  },
31
37
  "devDependencies": {
32
38
  "@react-native-community/netinfo": "^12.0.1",
@@ -34,8 +40,9 @@
34
40
  "expo-file-system": "^57.0.7",
35
41
  "expo-sqlite": "^57.0.4",
36
42
  "typescript": "~7.0.2",
43
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.2.0",
37
44
  "vite-plus": "1.2.0",
38
- "@party-stack/build-config": "0.1.3"
45
+ "@party-stack/build-config": "0.2.0"
39
46
  },
40
47
  "peerDependencies": {
41
48
  "@react-native-community/netinfo": "^12.0.1",
@@ -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;
@@ -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"}
@@ -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"}