@symbiote-native/secure-store 0.0.1 → 0.2.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.
@@ -1,4 +1,4 @@
1
1
  // @symbiote-native/secure-store/angular: the Angular entry over the framework-agnostic core.
2
2
  // Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
3
  // service, so this is a plain re-export.
4
- export * from '../core';
4
+ export * from '../core/index.js';
@@ -1 +1 @@
1
- export { AFTER_FIRST_UNLOCK, AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY, ALWAYS, ALWAYS_THIS_DEVICE_ONLY, WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, WHEN_UNLOCKED, WHEN_UNLOCKED_THIS_DEVICE_ONLY, isAvailableAsync, getItemAsync, getItem, setItemAsync, setItem, deleteItemAsync, canUseBiometricAuthentication, } from './secure-store';
1
+ export { AFTER_FIRST_UNLOCK, AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY, ALWAYS, ALWAYS_THIS_DEVICE_ONLY, WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, WHEN_UNLOCKED, WHEN_UNLOCKED_THIS_DEVICE_ONLY, isAvailableAsync, getItemAsync, getItem, setItemAsync, setItem, deleteItemAsync, canUseBiometricAuthentication, } from './secure-store.js';
@@ -1,5 +1,5 @@
1
1
  import { UnavailabilityError } from 'expo-modules-core';
2
- import { expoSecureStore } from './native-module';
2
+ import { expoSecureStore } from './native-module.js';
3
3
  const NATIVE_MODULE_NAME = 'expo-secure-store';
4
4
  // Alphanumerics plus ".", "-" and "_": the same set the native side validates against, so an
5
5
  // invalid key fails here with a readable message instead of deep inside the keychain call.
@@ -2,4 +2,4 @@
2
2
  // Upstream ships free functions and seven constants, no per-instance state and no event stream
3
3
  // — there is nothing for a hook to wrap, so this is a plain re-export, the same shape
4
4
  // packages/local-auth's React entry has for the same reason.
5
- export * from '../core';
5
+ export * from '../core/index.js';
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/secure-store/svelte: the Svelte entry over the framework-agnostic core.
2
+ // Upstream ships free functions and seven constants, no per-instance state and no event stream
3
+ // — there is nothing for a rune to wrap, so this is a plain re-export. Svelte's lifecycle
4
+ // bucket is `runes/` (adapters/svelte/src/runes — never React's `hooks` or Vue's
5
+ // `composables`), and this package has none to fill.
6
+ export * from '../core/index.js';
@@ -1,4 +1,4 @@
1
1
  // @symbiote-native/secure-store/vue: the Vue entry over the framework-agnostic core. Same
2
2
  // reasoning as the React entry — no per-instance state or event stream to wire onto Vue's
3
3
  // reactivity, so this is a plain re-export.
4
- export * from '../core';
4
+ export * from '../core/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/secure-store",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "expo-secure-store wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Encrypted key-value storage in the iOS Keychain and the Android Keystore, optionally gated behind biometrics.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,6 +30,10 @@
30
30
  "types": "./build/react/index.d.ts",
31
31
  "default": "./build/react/index.js"
32
32
  },
33
+ "./svelte": {
34
+ "types": "./build/svelte/index.d.ts",
35
+ "default": "./build/svelte/index.js"
36
+ },
33
37
  "./angular": {
34
38
  "types": "./build-ngc/angular/index.d.ts",
35
39
  "react-native": "./build-ngc/angular/index.js",
@@ -40,7 +44,10 @@
40
44
  "src",
41
45
  "build",
42
46
  "build-ngc",
43
- "native-link.json"
47
+ "native-link.json",
48
+ "!src/**/*.test.*",
49
+ "!src/**/*.spec.*",
50
+ "!src/**/*.detox.*"
44
51
  ],
45
52
  "publishConfig": {
46
53
  "access": "public"
@@ -50,15 +57,17 @@
50
57
  "expo-modules-core": "57.0.5"
51
58
  },
52
59
  "peerDependencies": {
60
+ "@symbiote-native/angular": ">=0.6.2",
53
61
  "@symbiote-native/engine": ">=0.1.7",
62
+ "@symbiote-native/react": ">=0.2.8",
63
+ "@symbiote-native/svelte": ">=0.1.0",
64
+ "@symbiote-native/vue": ">=0.3.8",
54
65
  "@angular/core": ">=20",
55
66
  "@vue/runtime-core": "^3.5.13",
56
67
  "react": ">=19.0.0",
57
68
  "react-native": ">=0.86",
58
- "vue": ">=3.5.0",
59
- "@symbiote-native/react": "0.2.8",
60
- "@symbiote-native/angular": "0.6.1",
61
- "@symbiote-native/vue": "0.3.8"
69
+ "svelte": ">=5.56.0",
70
+ "vue": ">=3.5.0"
62
71
  },
63
72
  "peerDependenciesMeta": {
64
73
  "@symbiote-native/angular": {
@@ -67,6 +76,9 @@
67
76
  "@symbiote-native/react": {
68
77
  "optional": true
69
78
  },
79
+ "@symbiote-native/svelte": {
80
+ "optional": true
81
+ },
70
82
  "@symbiote-native/vue": {
71
83
  "optional": true
72
84
  },
@@ -79,24 +91,29 @@
79
91
  "react": {
80
92
  "optional": true
81
93
  },
94
+ "svelte": {
95
+ "optional": true
96
+ },
82
97
  "vue": {
83
98
  "optional": true
84
99
  }
85
100
  },
86
101
  "devDependencies": {
87
- "@angular/compiler": "^22",
88
- "@angular/compiler-cli": "^22",
89
- "@angular/core": "^22",
102
+ "@angular/compiler": "~22.0.8",
103
+ "@angular/compiler-cli": "~22.0.8",
104
+ "@angular/core": "~22.0.8",
90
105
  "@types/node": "^26.0.0",
91
106
  "@types/react": "^19.2.0",
92
107
  "@vue/runtime-core": "^3.5.13",
93
108
  "react": "19.2.3",
109
+ "svelte": "^5.56.0",
94
110
  "typescript": "~6.0.0",
95
- "@symbiote-native/engine": "0.1.7",
111
+ "@symbiote-native/angular": "0.7.0",
112
+ "@symbiote-native/engine": "0.2.0",
113
+ "@symbiote-native/react": "0.3.0",
114
+ "@symbiote-native/svelte": "0.2.0",
96
115
  "@symbiote-native/test-utils": "0.1.6",
97
- "@symbiote-native/react": "0.2.8",
98
- "@symbiote-native/angular": "0.6.1",
99
- "@symbiote-native/vue": "0.3.8"
116
+ "@symbiote-native/vue": "0.4.0"
100
117
  },
101
118
  "scripts": {
102
119
  "typecheck": "tsc --build",
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/secure-store/svelte: the Svelte entry over the framework-agnostic core.
2
+ // Upstream ships free functions and seven constants, no per-instance state and no event stream
3
+ // — there is nothing for a rune to wrap, so this is a plain re-export. Svelte's lifecycle
4
+ // bucket is `runes/` (adapters/svelte/src/runes — never React's `hooks` or Vue's
5
+ // `composables`), and this package has none to fill.
6
+ export * from '../core';
@@ -1,146 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const FAKE_NATIVE_SECURE_STORE = {
4
- AFTER_FIRST_UNLOCK: 1,
5
- WHEN_UNLOCKED: 5,
6
- getValueWithKeyAsync: vi.fn(async () => 'stored'),
7
- getValueWithKeySync: vi.fn(() => 'stored'),
8
- setValueWithKeyAsync: vi.fn(async () => true),
9
- setValueWithKeySync: vi.fn(() => true),
10
- deleteValueWithKeyAsync: vi.fn(async () => undefined),
11
- canUseBiometricAuthentication: vi.fn(() => true),
12
- };
13
-
14
- // The real ExpoSecureStore native module only exists on device — resolving it via
15
- // requireNativeModule() at import time would throw in this headless run, so the module-lookup
16
- // file is faked in place of expo-modules-core's runtime resolution, the same pattern
17
- // packages/local-auth/src/core/local-authentication.test.ts uses.
18
- vi.mock('./native-module', () => ({
19
- expoSecureStore: FAKE_NATIVE_SECURE_STORE,
20
- }));
21
-
22
- // expo-modules-core's real entry transitively imports 'react-native', whose Flow-typed source
23
- // Vitest's Oxc transform can't parse — so only the members used as values are faked.
24
- vi.mock('expo-modules-core', () => ({
25
- UnavailabilityError: class UnavailabilityError extends Error {
26
- constructor(moduleName: string, propertyName: string) {
27
- super(`${propertyName} is not available on ${moduleName}`);
28
- }
29
- },
30
- }));
31
-
32
- const {
33
- AFTER_FIRST_UNLOCK,
34
- WHEN_UNLOCKED,
35
- isAvailableAsync,
36
- getItemAsync,
37
- getItem,
38
- setItemAsync,
39
- setItem,
40
- deleteItemAsync,
41
- canUseBiometricAuthentication,
42
- } = await import('./secure-store');
43
-
44
- afterEach(() => {
45
- vi.clearAllMocks();
46
- });
47
-
48
- describe('keychain accessibility constants', () => {
49
- it('come from the native module rather than being hardcoded', () => {
50
- expect(AFTER_FIRST_UNLOCK).toBe(1);
51
- expect(WHEN_UNLOCKED).toBe(5);
52
- });
53
- });
54
-
55
- describe('isAvailableAsync', () => {
56
- it('reports availability from the presence of the native read method', async () => {
57
- await expect(isAvailableAsync()).resolves.toBe(true);
58
- });
59
- });
60
-
61
- describe('getItemAsync', () => {
62
- it('passes the key and options straight through', async () => {
63
- await expect(getItemAsync('token', { keychainService: 'auth' })).resolves.toBe('stored');
64
- expect(FAKE_NATIVE_SECURE_STORE.getValueWithKeyAsync).toHaveBeenCalledWith('token', {
65
- keychainService: 'auth',
66
- });
67
- });
68
-
69
- it('defaults the options to an empty object', async () => {
70
- await getItemAsync('token');
71
- expect(FAKE_NATIVE_SECURE_STORE.getValueWithKeyAsync).toHaveBeenCalledWith('token', {});
72
- });
73
-
74
- it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
75
- const { getValueWithKeyAsync: native } = FAKE_NATIVE_SECURE_STORE;
76
- // @ts-expect-error -- simulating a platform where the native module has no such method
77
- FAKE_NATIVE_SECURE_STORE.getValueWithKeyAsync = undefined;
78
-
79
- await expect(getItemAsync('token')).rejects.toThrow(
80
- 'getItemAsync is not available on expo-secure-store',
81
- );
82
-
83
- FAKE_NATIVE_SECURE_STORE.getValueWithKeyAsync = native;
84
- });
85
- });
86
-
87
- describe('setItemAsync', () => {
88
- it('passes value, key and options in the native argument order', async () => {
89
- await setItemAsync('token', 'secret', { requireAuthentication: true });
90
- expect(FAKE_NATIVE_SECURE_STORE.setValueWithKeyAsync).toHaveBeenCalledWith('secret', 'token', {
91
- requireAuthentication: true,
92
- });
93
- });
94
- });
95
-
96
- describe('the synchronous pair', () => {
97
- it('reads and writes through the sync native methods', () => {
98
- expect(getItem('token')).toBe('stored');
99
- setItem('token', 'secret');
100
- expect(FAKE_NATIVE_SECURE_STORE.getValueWithKeySync).toHaveBeenCalledWith('token', {});
101
- expect(FAKE_NATIVE_SECURE_STORE.setValueWithKeySync).toHaveBeenCalledWith(
102
- 'secret',
103
- 'token',
104
- {},
105
- );
106
- });
107
- });
108
-
109
- describe('deleteItemAsync', () => {
110
- it('delegates to the native module', async () => {
111
- await deleteItemAsync('token');
112
- expect(FAKE_NATIVE_SECURE_STORE.deleteValueWithKeyAsync).toHaveBeenCalledWith('token', {});
113
- });
114
- });
115
-
116
- describe('canUseBiometricAuthentication', () => {
117
- it('delegates to the native module', () => {
118
- expect(canUseBiometricAuthentication()).toBe(true);
119
- });
120
- });
121
-
122
- // Validation happens before the native call so a bad key fails with a readable message here
123
- // rather than deep inside the keychain, and the native module is never handed junk.
124
- describe('key and value validation', () => {
125
- it.each([
126
- ['', 'empty'],
127
- ['has space', 'a space'],
128
- ['sla/sh', 'a slash'],
129
- ['💥', 'an emoji'],
130
- ])('rejects %j (%s)', async key => {
131
- await expect(getItemAsync(key)).rejects.toThrow(/Invalid key provided to SecureStore/);
132
- expect(FAKE_NATIVE_SECURE_STORE.getValueWithKeyAsync).not.toHaveBeenCalled();
133
- });
134
-
135
- it.each(['token', 'com.example.token', 'my-token', 'my_token'])('accepts %j', async key => {
136
- await expect(getItemAsync(key)).resolves.toBe('stored');
137
- });
138
-
139
- it('rejects a non-string value before reaching the native module', async () => {
140
- // @ts-expect-error -- the guard exists precisely for callers without type checking
141
- await expect(setItemAsync('token', { a: 1 })).rejects.toThrow(
142
- /Values must be strings; consider JSON-encoding/,
143
- );
144
- expect(FAKE_NATIVE_SECURE_STORE.setValueWithKeyAsync).not.toHaveBeenCalled();
145
- });
146
- });