@symbiote-native/standard-web-crypto 0.0.1
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/LICENSE +21 -0
- package/README.md +95 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +6 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +1 -0
- package/build/core/web-crypto.d.ts +6 -0
- package/build/core/web-crypto.js +53 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.js +7 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +6 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +7 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/index.d.ts +2 -0
- package/build-ngc/core/index.js +2 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/web-crypto.d.ts +6 -0
- package/build-ngc/core/web-crypto.js +54 -0
- package/build-ngc/core/web-crypto.js.map +1 -0
- package/package.json +105 -0
- package/src/angular/index.ts +6 -0
- package/src/core/index.ts +2 -0
- package/src/core/web-crypto.test.ts +91 -0
- package/src/core/web-crypto.ts +67 -0
- package/src/react/index.ts +7 -0
- package/src/vue/index.ts +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A. Prokopenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @symbiote-native/standard-web-crypto
|
|
2
|
+
|
|
3
|
+
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
+
[`expo-standard-web-crypto`](https://github.com/expo/expo/tree/main/packages/expo-standard-web-crypto)
|
|
5
|
+
— a partial W3C [Web Crypto API](https://www.w3.org/TR/WebCryptoAPI/) polyfill exposing
|
|
6
|
+
`crypto.getRandomValues` — usable from **every** adapter, React, Vue, and Angular, not just React.
|
|
7
|
+
Unlike this repo's other Expo ports, this package needs **no native module of its own** — upstream
|
|
8
|
+
is a ~15-line pure-JS shim that delegates straight to `expo-crypto`'s own `getRandomValues`; this
|
|
9
|
+
port delegates to [`@symbiote-native/crypto`](../crypto)'s `getRandomValues` instead, since this
|
|
10
|
+
repo already ships that native random source as a sibling package. Like `@symbiote-native/crypto`,
|
|
11
|
+
every export here is a plain function/object with no per-instance state or event stream, so there
|
|
12
|
+
is no hook/composable/service to wrap — the React, Vue, and Angular entry points are plain
|
|
13
|
+
re-exports of the same `core`.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @symbiote-native/standard-web-crypto @symbiote-native/crypto
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`@symbiote-native/crypto` comes along as a regular dependency and does the actual native random-byte
|
|
22
|
+
generation — see [its README](../crypto/README.md) for `expo-crypto`'s own native autolinking
|
|
23
|
+
requirements (already satisfied in any app that already wires up `@symbiote-native/crypto` or
|
|
24
|
+
`@symbiote-native/device`).
|
|
25
|
+
|
|
26
|
+
No further native wiring is needed for this package itself — it has no native module of its own.
|
|
27
|
+
|
|
28
|
+
## Shape
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/core/ web-crypto.ts — the Crypto class + webCrypto singleton (default export) and
|
|
32
|
+
polyfillWebCrypto(), delegating to @symbiote-native/crypto's getRandomValues.
|
|
33
|
+
src/react/ @symbiote-native/standard-web-crypto/react — export * from '../core'
|
|
34
|
+
src/vue/ @symbiote-native/standard-web-crypto/vue — export * from '../core'
|
|
35
|
+
src/angular/ @symbiote-native/standard-web-crypto/angular — export * from '../core'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
No per-adapter lifecycle wrapper exists because there's nothing to subscribe to or clean up —
|
|
39
|
+
each adapter entry is a single-file re-export.
|
|
40
|
+
|
|
41
|
+
## Use it
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import webCrypto, { polyfillWebCrypto } from '@symbiote-native/standard-web-crypto';
|
|
45
|
+
|
|
46
|
+
// Reach the polyfill (or a real globalThis.crypto, if one already exists) directly:
|
|
47
|
+
const bytes = new Uint8Array(16);
|
|
48
|
+
webCrypto.getRandomValues(bytes);
|
|
49
|
+
|
|
50
|
+
// Or install it as globalThis.crypto for any library that expects the Web Crypto API to
|
|
51
|
+
// already be present — a no-op if globalThis.crypto is already defined:
|
|
52
|
+
polyfillWebCrypto();
|
|
53
|
+
crypto.getRandomValues(bytes);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The React/Vue/Angular entry points re-export the identical surface:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import webCrypto, { polyfillWebCrypto } from '@symbiote-native/standard-web-crypto/react';
|
|
60
|
+
import webCrypto, { polyfillWebCrypto } from '@symbiote-native/standard-web-crypto/vue';
|
|
61
|
+
import webCrypto, { polyfillWebCrypto } from '@symbiote-native/standard-web-crypto/angular';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const webCrypto: {
|
|
68
|
+
getRandomValues<TArray extends ArrayBufferView>(values: TArray): TArray;
|
|
69
|
+
};
|
|
70
|
+
export default webCrypto;
|
|
71
|
+
|
|
72
|
+
function polyfillWebCrypto(): void;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Plus the `IWebCrypto` type describing the shape above.
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- **`webCrypto` is resolved once, at module-load time**: if `globalThis.crypto` already exists
|
|
80
|
+
(a real Web Crypto implementation), `webCrypto` *is* that object; otherwise it's this package's
|
|
81
|
+
own `Crypto` class instance backed by `@symbiote-native/crypto`. React Native has no reliable
|
|
82
|
+
`window` global, so this port checks/defines `globalThis.crypto` rather than upstream's `window`.
|
|
83
|
+
- **`getRandomValues` only accepts the integer TypedArrays `@symbiote-native/crypto` can hand to
|
|
84
|
+
its native module** (`Int8Array`/`Uint8Array`/`Int16Array`/`Uint16Array`/`Int32Array`/
|
|
85
|
+
`Uint32Array`) — passing any other `ArrayBufferView` (a `DataView`, `Uint8ClampedArray`, a
|
|
86
|
+
`Float32Array`, …) throws a `TypeError`, the same way upstream's own `getRandomValues` rejects an
|
|
87
|
+
unsupported view.
|
|
88
|
+
- `polyfillWebCrypto()` is a no-op when `globalThis.crypto` is already defined — it never
|
|
89
|
+
overwrites an existing implementation.
|
|
90
|
+
|
|
91
|
+
## Test it
|
|
92
|
+
|
|
93
|
+
Every export here is a pure function/object surface, never a view or per-instance state — no
|
|
94
|
+
Fabric/Descriptor angle at all. Tests mock `@symbiote-native/crypto`'s `getRandomValues` directly
|
|
95
|
+
(`src/core/web-crypto.test.ts`, `vitest`) — no `installFabric()`, no ViewConfig.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/angular: the Angular entry over the framework-agnostic
|
|
2
|
+
// core. Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in
|
|
3
|
+
// a service, so this is a plain re-export. `export *` never forwards a default export (ES module
|
|
4
|
+
// semantics), so `webCrypto` needs its own explicit named re-export alongside it.
|
|
5
|
+
export * from '../core';
|
|
6
|
+
export { default as webCrypto } from '../core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, polyfillWebCrypto } from './web-crypto';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Ported from expo-standard-web-crypto (upstream: packages/expo-standard-web-crypto/src/index.ts,
|
|
2
|
+
// verified identical on origin/main and origin/sdk-57) — a partial W3C Web Crypto API polyfill
|
|
3
|
+
// exposing `crypto.getRandomValues`. Upstream delegates to expo-crypto's own getRandomValues; this
|
|
4
|
+
// port delegates to @symbiote-native/crypto's getRandomValues instead, which already wraps the
|
|
5
|
+
// same native random source, so there is no native call left to re-wrap here. React Native has no
|
|
6
|
+
// reliable `window` global, so `globalThis` replaces every `window`/bare `crypto` reference
|
|
7
|
+
// upstream used.
|
|
8
|
+
import { getRandomValues as getNativeRandomValues } from '@symbiote-native/crypto';
|
|
9
|
+
// @symbiote-native/crypto's getRandomValues only accepts the integer TypedArrays it can hand to
|
|
10
|
+
// the native module (ITypedArray) — narrower than the W3C spec's ArrayBufferView. This guard
|
|
11
|
+
// narrows the incoming view at the one call site that needs it, so the class below can still
|
|
12
|
+
// expose the full ArrayBufferView-typed method the upstream Crypto class does.
|
|
13
|
+
function isSupportedTypedArray(value) {
|
|
14
|
+
return (value instanceof Int8Array ||
|
|
15
|
+
value instanceof Uint8Array ||
|
|
16
|
+
value instanceof Int16Array ||
|
|
17
|
+
value instanceof Uint16Array ||
|
|
18
|
+
value instanceof Int32Array ||
|
|
19
|
+
value instanceof Uint32Array);
|
|
20
|
+
}
|
|
21
|
+
class Crypto {
|
|
22
|
+
getRandomValues(values) {
|
|
23
|
+
if (!isSupportedTypedArray(values)) {
|
|
24
|
+
throw new TypeError('The provided ArrayBuffer view is not a supported integer-typed array');
|
|
25
|
+
}
|
|
26
|
+
return getNativeRandomValues(values);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isWebCrypto(value) {
|
|
30
|
+
if (typeof value !== 'object' || value === null) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return 'getRandomValues' in value && typeof value.getRandomValues === 'function';
|
|
34
|
+
}
|
|
35
|
+
// No ambient `declare global { var crypto }` here on purpose — it would collide with lib.dom.d.ts's
|
|
36
|
+
// own `crypto` global the moment any consumer's tsconfig pulls in the DOM lib (as this package's
|
|
37
|
+
// own Angular build does), producing a duplicate-declaration error. Reflect.get sidesteps that:
|
|
38
|
+
// it reads an untyped global property without asserting one into existence.
|
|
39
|
+
function readGlobalCrypto() {
|
|
40
|
+
const value = Reflect.get(globalThis, 'crypto');
|
|
41
|
+
return isWebCrypto(value) ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
const webCrypto = readGlobalCrypto() ?? new Crypto();
|
|
44
|
+
export default webCrypto;
|
|
45
|
+
export function polyfillWebCrypto() {
|
|
46
|
+
if (readGlobalCrypto() === undefined) {
|
|
47
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
48
|
+
configurable: true,
|
|
49
|
+
enumerable: true,
|
|
50
|
+
get: () => webCrypto,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// The polyfill has no per-instance state or event stream — nothing for a hook to wrap — so this
|
|
3
|
+
// is a plain re-export, mirroring @symbiote-native/device's and @symbiote-native/crypto's React
|
|
4
|
+
// entries. `export *` never forwards a default export (ES module semantics), so `webCrypto` needs
|
|
5
|
+
// its own explicit named re-export alongside it.
|
|
6
|
+
export * from '../core';
|
|
7
|
+
export { default as webCrypto } from '../core';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/vue: the Vue entry over the framework-agnostic core. Same
|
|
2
|
+
// reasoning as the React entry — no per-instance state or event stream to wire onto Vue's
|
|
3
|
+
// reactivity, so this is a plain re-export. `export *` never forwards a default export (ES module
|
|
4
|
+
// semantics), so `webCrypto` needs its own explicit named re-export alongside it.
|
|
5
|
+
export * from '../core';
|
|
6
|
+
export { default as webCrypto } from '../core';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/angular: the Angular entry over the framework-agnostic
|
|
2
|
+
// core. Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in
|
|
3
|
+
// a service, so this is a plain re-export. `export *` never forwards a default export (ES module
|
|
4
|
+
// semantics), so `webCrypto` needs its own explicit named re-export alongside it.
|
|
5
|
+
export * from '../core';
|
|
6
|
+
export { default as webCrypto } from '../core';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,mGAAmG;AACnG,iGAAiG;AACjG,kFAAkF;AAClF,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Ported from expo-standard-web-crypto (upstream: packages/expo-standard-web-crypto/src/index.ts,
|
|
2
|
+
// verified identical on origin/main and origin/sdk-57) — a partial W3C Web Crypto API polyfill
|
|
3
|
+
// exposing `crypto.getRandomValues`. Upstream delegates to expo-crypto's own getRandomValues; this
|
|
4
|
+
// port delegates to @symbiote-native/crypto's getRandomValues instead, which already wraps the
|
|
5
|
+
// same native random source, so there is no native call left to re-wrap here. React Native has no
|
|
6
|
+
// reliable `window` global, so `globalThis` replaces every `window`/bare `crypto` reference
|
|
7
|
+
// upstream used.
|
|
8
|
+
import { getRandomValues as getNativeRandomValues } from '@symbiote-native/crypto';
|
|
9
|
+
// @symbiote-native/crypto's getRandomValues only accepts the integer TypedArrays it can hand to
|
|
10
|
+
// the native module (ITypedArray) — narrower than the W3C spec's ArrayBufferView. This guard
|
|
11
|
+
// narrows the incoming view at the one call site that needs it, so the class below can still
|
|
12
|
+
// expose the full ArrayBufferView-typed method the upstream Crypto class does.
|
|
13
|
+
function isSupportedTypedArray(value) {
|
|
14
|
+
return (value instanceof Int8Array ||
|
|
15
|
+
value instanceof Uint8Array ||
|
|
16
|
+
value instanceof Int16Array ||
|
|
17
|
+
value instanceof Uint16Array ||
|
|
18
|
+
value instanceof Int32Array ||
|
|
19
|
+
value instanceof Uint32Array);
|
|
20
|
+
}
|
|
21
|
+
class Crypto {
|
|
22
|
+
getRandomValues(values) {
|
|
23
|
+
if (!isSupportedTypedArray(values)) {
|
|
24
|
+
throw new TypeError('The provided ArrayBuffer view is not a supported integer-typed array');
|
|
25
|
+
}
|
|
26
|
+
return getNativeRandomValues(values);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isWebCrypto(value) {
|
|
30
|
+
if (typeof value !== 'object' || value === null) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return 'getRandomValues' in value && typeof value.getRandomValues === 'function';
|
|
34
|
+
}
|
|
35
|
+
// No ambient `declare global { var crypto }` here on purpose — it would collide with lib.dom.d.ts's
|
|
36
|
+
// own `crypto` global the moment any consumer's tsconfig pulls in the DOM lib (as this package's
|
|
37
|
+
// own Angular build does), producing a duplicate-declaration error. Reflect.get sidesteps that:
|
|
38
|
+
// it reads an untyped global property without asserting one into existence.
|
|
39
|
+
function readGlobalCrypto() {
|
|
40
|
+
const value = Reflect.get(globalThis, 'crypto');
|
|
41
|
+
return isWebCrypto(value) ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
const webCrypto = readGlobalCrypto() ?? new Crypto();
|
|
44
|
+
export default webCrypto;
|
|
45
|
+
export function polyfillWebCrypto() {
|
|
46
|
+
if (readGlobalCrypto() === undefined) {
|
|
47
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
48
|
+
configurable: true,
|
|
49
|
+
enumerable: true,
|
|
50
|
+
get: () => webCrypto,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=web-crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-crypto.js","sourceRoot":"","sources":["../../src/core/web-crypto.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,+FAA+F;AAC/F,mGAAmG;AACnG,+FAA+F;AAC/F,kGAAkG;AAClG,4FAA4F;AAC5F,iBAAiB;AACjB,OAAO,EAAE,eAAe,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAOnF,gGAAgG;AAChG,6FAA6F;AAC7F,6FAA6F;AAC7F,+EAA+E;AAC/E,SAAS,qBAAqB,CAAC,KAAsB;IACnD,OAAO,CACL,KAAK,YAAY,SAAS;QAC1B,KAAK,YAAY,UAAU;QAC3B,KAAK,YAAY,UAAU;QAC3B,KAAK,YAAY,WAAW;QAC5B,KAAK,YAAY,UAAU;QAC3B,KAAK,YAAY,WAAW,CAC7B,CAAC;AACJ,CAAC;AAED,MAAM,MAAM;IACV,eAAe,CAAiC,MAAc;QAC5D,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,SAAS,CAAC,sEAAsE,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;CACF;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,iBAAiB,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,eAAe,KAAK,UAAU,CAAC;AACnF,CAAC;AAED,oGAAoG;AACpG,iGAAiG;AACjG,gGAAgG;AAChG,4EAA4E;AAC5E,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACzD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,MAAM,SAAS,GAAe,gBAAgB,EAAE,IAAI,IAAI,MAAM,EAAE,CAAC;AAEjE,eAAe,SAAS,CAAC;AAEzB,MAAM,UAAU,iBAAiB;IAC/B,IAAI,gBAAgB,EAAE,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE;YAC1C,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,IAAI;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;SACrB,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/standard-web-crypto",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "expo-standard-web-crypto ported for SymbioteNative — a Web Crypto API getRandomValues polyfill built on @symbiote-native/crypto's native random source.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/standard-web-crypto"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/standard-web-crypto#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./build/core/index.js",
|
|
18
|
+
"module": "./build/core/index.js",
|
|
19
|
+
"types": "./build/core/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./build/core/index.d.ts",
|
|
23
|
+
"default": "./build/core/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./vue": {
|
|
26
|
+
"types": "./build/vue/index.d.ts",
|
|
27
|
+
"default": "./build/vue/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./build/react/index.d.ts",
|
|
31
|
+
"default": "./build/react/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./angular": {
|
|
34
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
35
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
36
|
+
"default": "./src/angular/index.ts"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"build",
|
|
42
|
+
"build-ngc"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@symbiote-native/crypto": "0.0.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@symbiote-native/engine": ">=0.1.7",
|
|
52
|
+
"@angular/core": ">=20",
|
|
53
|
+
"@vue/runtime-core": "^3.5.13",
|
|
54
|
+
"react": ">=19.0.0",
|
|
55
|
+
"react-native": ">=0.86",
|
|
56
|
+
"vue": ">=3.5.0",
|
|
57
|
+
"@symbiote-native/angular": "0.6.1",
|
|
58
|
+
"@symbiote-native/react": "0.2.8",
|
|
59
|
+
"@symbiote-native/vue": "0.3.8"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@symbiote-native/angular": {
|
|
63
|
+
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"@symbiote-native/react": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"@symbiote-native/vue": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@angular/core": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@vue/runtime-core": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"vue": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"@angular/compiler": "^22",
|
|
86
|
+
"@angular/compiler-cli": "^22",
|
|
87
|
+
"@angular/core": "^22",
|
|
88
|
+
"@types/node": "^26.0.0",
|
|
89
|
+
"@types/react": "^19.2.0",
|
|
90
|
+
"@vue/runtime-core": "^3.5.13",
|
|
91
|
+
"react": "19.2.3",
|
|
92
|
+
"typescript": "~6.0.0",
|
|
93
|
+
"@symbiote-native/test-utils": "0.1.6",
|
|
94
|
+
"@symbiote-native/engine": "0.1.7",
|
|
95
|
+
"@symbiote-native/react": "0.2.8",
|
|
96
|
+
"@symbiote-native/angular": "0.6.1",
|
|
97
|
+
"@symbiote-native/vue": "0.3.8"
|
|
98
|
+
},
|
|
99
|
+
"scripts": {
|
|
100
|
+
"typecheck": "tsc --build",
|
|
101
|
+
"clean": "rm -rf build-ngc",
|
|
102
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
103
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/angular: the Angular entry over the framework-agnostic
|
|
2
|
+
// core. Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in
|
|
3
|
+
// a service, so this is a plain re-export. `export *` never forwards a default export (ES module
|
|
4
|
+
// semantics), so `webCrypto` needs its own explicit named re-export alongside it.
|
|
5
|
+
export * from '../core';
|
|
6
|
+
export { default as webCrypto } from '../core';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const getRandomValues = vi.fn((typedArray: Uint8Array) => {
|
|
4
|
+
typedArray.fill(7);
|
|
5
|
+
return typedArray;
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// @symbiote-native/crypto resolves the real native module via requireNativeModule() at import
|
|
9
|
+
// time, which would throw in this headless test run — faked here in place of the sibling
|
|
10
|
+
// package, the same pattern packages/crypto/src/core/crypto.test.ts uses for expo-modules-core.
|
|
11
|
+
vi.mock('@symbiote-native/crypto', () => ({
|
|
12
|
+
getRandomValues,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Node (unlike React Native) ships its own native `globalThis.crypto` — deleted before every test
|
|
16
|
+
// so `web-crypto.ts`'s module-load-time feature detection sees the same "no crypto global" state
|
|
17
|
+
// it would on-device, not Node's own WebCrypto implementation.
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
Reflect.deleteProperty(globalThis, 'crypto');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.clearAllMocks();
|
|
24
|
+
Reflect.deleteProperty(globalThis, 'crypto');
|
|
25
|
+
vi.resetModules();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('webCrypto.getRandomValues', () => {
|
|
29
|
+
it('delegates a supported typed array to @symbiote-native/crypto', async () => {
|
|
30
|
+
const { default: webCrypto } = await import('./web-crypto');
|
|
31
|
+
const typedArray = new Uint8Array(4);
|
|
32
|
+
|
|
33
|
+
const result = webCrypto.getRandomValues(typedArray);
|
|
34
|
+
|
|
35
|
+
expect(result).toBe(typedArray);
|
|
36
|
+
expect(getRandomValues).toHaveBeenCalledWith(typedArray);
|
|
37
|
+
expect(Array.from(typedArray)).toEqual([7, 7, 7, 7]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('throws a TypeError for a view outside the supported integer TypedArrays', async () => {
|
|
41
|
+
const { default: webCrypto } = await import('./web-crypto');
|
|
42
|
+
|
|
43
|
+
expect(() => webCrypto.getRandomValues(new DataView(new ArrayBuffer(4)))).toThrow(TypeError);
|
|
44
|
+
expect(getRandomValues).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('webCrypto singleton', () => {
|
|
49
|
+
it('is the polyfill instance when globalThis.crypto is absent', async () => {
|
|
50
|
+
const { default: webCrypto } = await import('./web-crypto');
|
|
51
|
+
|
|
52
|
+
expect(typeof webCrypto.getRandomValues).toBe('function');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('reuses an existing globalThis.crypto instead of the polyfill', async () => {
|
|
56
|
+
const existingCrypto = { getRandomValues: vi.fn() };
|
|
57
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
58
|
+
configurable: true,
|
|
59
|
+
value: existingCrypto,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const { default: webCrypto } = await import('./web-crypto');
|
|
63
|
+
|
|
64
|
+
expect(webCrypto).toBe(existingCrypto);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('polyfillWebCrypto', () => {
|
|
69
|
+
it('defines globalThis.crypto when it is absent', async () => {
|
|
70
|
+
const { default: webCrypto, polyfillWebCrypto } = await import('./web-crypto');
|
|
71
|
+
|
|
72
|
+
expect(Reflect.get(globalThis, 'crypto')).toBeUndefined();
|
|
73
|
+
|
|
74
|
+
polyfillWebCrypto();
|
|
75
|
+
|
|
76
|
+
expect(Reflect.get(globalThis, 'crypto')).toBe(webCrypto);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('does not overwrite an existing globalThis.crypto', async () => {
|
|
80
|
+
const existingCrypto = { getRandomValues: vi.fn() };
|
|
81
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
82
|
+
configurable: true,
|
|
83
|
+
value: existingCrypto,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const { polyfillWebCrypto } = await import('./web-crypto');
|
|
87
|
+
polyfillWebCrypto();
|
|
88
|
+
|
|
89
|
+
expect(Reflect.get(globalThis, 'crypto')).toBe(existingCrypto);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Ported from expo-standard-web-crypto (upstream: packages/expo-standard-web-crypto/src/index.ts,
|
|
2
|
+
// verified identical on origin/main and origin/sdk-57) — a partial W3C Web Crypto API polyfill
|
|
3
|
+
// exposing `crypto.getRandomValues`. Upstream delegates to expo-crypto's own getRandomValues; this
|
|
4
|
+
// port delegates to @symbiote-native/crypto's getRandomValues instead, which already wraps the
|
|
5
|
+
// same native random source, so there is no native call left to re-wrap here. React Native has no
|
|
6
|
+
// reliable `window` global, so `globalThis` replaces every `window`/bare `crypto` reference
|
|
7
|
+
// upstream used.
|
|
8
|
+
import { getRandomValues as getNativeRandomValues } from '@symbiote-native/crypto';
|
|
9
|
+
import type { ITypedArray } from '@symbiote-native/crypto';
|
|
10
|
+
|
|
11
|
+
export type IWebCrypto = {
|
|
12
|
+
getRandomValues<TArray extends ArrayBufferView>(values: TArray): TArray;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// @symbiote-native/crypto's getRandomValues only accepts the integer TypedArrays it can hand to
|
|
16
|
+
// the native module (ITypedArray) — narrower than the W3C spec's ArrayBufferView. This guard
|
|
17
|
+
// narrows the incoming view at the one call site that needs it, so the class below can still
|
|
18
|
+
// expose the full ArrayBufferView-typed method the upstream Crypto class does.
|
|
19
|
+
function isSupportedTypedArray(value: ArrayBufferView): value is ITypedArray {
|
|
20
|
+
return (
|
|
21
|
+
value instanceof Int8Array ||
|
|
22
|
+
value instanceof Uint8Array ||
|
|
23
|
+
value instanceof Int16Array ||
|
|
24
|
+
value instanceof Uint16Array ||
|
|
25
|
+
value instanceof Int32Array ||
|
|
26
|
+
value instanceof Uint32Array
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class Crypto implements IWebCrypto {
|
|
31
|
+
getRandomValues<TArray extends ArrayBufferView>(values: TArray): TArray {
|
|
32
|
+
if (!isSupportedTypedArray(values)) {
|
|
33
|
+
throw new TypeError('The provided ArrayBuffer view is not a supported integer-typed array');
|
|
34
|
+
}
|
|
35
|
+
return getNativeRandomValues(values);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isWebCrypto(value: unknown): value is IWebCrypto {
|
|
40
|
+
if (typeof value !== 'object' || value === null) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return 'getRandomValues' in value && typeof value.getRandomValues === 'function';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// No ambient `declare global { var crypto }` here on purpose — it would collide with lib.dom.d.ts's
|
|
47
|
+
// own `crypto` global the moment any consumer's tsconfig pulls in the DOM lib (as this package's
|
|
48
|
+
// own Angular build does), producing a duplicate-declaration error. Reflect.get sidesteps that:
|
|
49
|
+
// it reads an untyped global property without asserting one into existence.
|
|
50
|
+
function readGlobalCrypto(): IWebCrypto | undefined {
|
|
51
|
+
const value: unknown = Reflect.get(globalThis, 'crypto');
|
|
52
|
+
return isWebCrypto(value) ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const webCrypto: IWebCrypto = readGlobalCrypto() ?? new Crypto();
|
|
56
|
+
|
|
57
|
+
export default webCrypto;
|
|
58
|
+
|
|
59
|
+
export function polyfillWebCrypto(): void {
|
|
60
|
+
if (readGlobalCrypto() === undefined) {
|
|
61
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
62
|
+
configurable: true,
|
|
63
|
+
enumerable: true,
|
|
64
|
+
get: () => webCrypto,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// The polyfill has no per-instance state or event stream — nothing for a hook to wrap — so this
|
|
3
|
+
// is a plain re-export, mirroring @symbiote-native/device's and @symbiote-native/crypto's React
|
|
4
|
+
// entries. `export *` never forwards a default export (ES module semantics), so `webCrypto` needs
|
|
5
|
+
// its own explicit named re-export alongside it.
|
|
6
|
+
export * from '../core';
|
|
7
|
+
export { default as webCrypto } from '../core';
|
package/src/vue/index.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/standard-web-crypto/vue: the Vue entry over the framework-agnostic core. Same
|
|
2
|
+
// reasoning as the React entry — no per-instance state or event stream to wire onto Vue's
|
|
3
|
+
// reactivity, so this is a plain re-export. `export *` never forwards a default export (ES module
|
|
4
|
+
// semantics), so `webCrypto` needs its own explicit named re-export alongside it.
|
|
5
|
+
export * from '../core';
|
|
6
|
+
export { default as webCrypto } from '../core';
|