@latchway/react-native 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +8 -1
- package/babel.cjs +22 -0
- package/babel.d.cts +7 -0
- package/docs/langchain.md +135 -0
- package/lib/polyfills.d.ts +8 -0
- package/lib/polyfills.d.ts.map +1 -0
- package/lib/polyfills.js +56 -0
- package/lib/polyfills.js.map +1 -0
- package/lib/runtime-symbols.d.ts +2 -0
- package/lib/runtime-symbols.d.ts.map +1 -0
- package/lib/runtime-symbols.js +9 -0
- package/lib/runtime-symbols.js.map +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +27 -5
- package/release-compatibility.json +1 -1
- package/src/polyfills.ts +50 -0
- package/src/runtime-symbols.ts +9 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ Versioning once package publication begins.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.0] - 2026-09-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Explicit `@latchway/react-native/polyfills` bootstrap for Hermes async symbols,
|
|
15
|
+
streaming UTF-8 decoding, web streams, URL and abort compatibility. Complete
|
|
16
|
+
existing globals are retained; the ordinary SDK import does not install it.
|
|
17
|
+
- Optional `withLatchwayBabel` configuration helper for LangChain class and
|
|
18
|
+
export-namespace compatibility without depending on LangChain at runtime.
|
|
19
|
+
- Standalone npm-only LatchwayChat with Firebase authentication, temporary
|
|
20
|
+
multi-turn LangChain weather tools, direct fetch, cancellation and diagnostics.
|
|
21
|
+
- Isolated runtime regressions and a LangChain quickstart.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Metro now resolves the published built JavaScript entry without a custom
|
|
26
|
+
`.js`-to-TypeScript resolver. Native Codegen retains its original TS schema.
|
|
27
|
+
|
|
28
|
+
### Compatibility
|
|
29
|
+
|
|
30
|
+
- Shared client, iOS App Attest and Android SDK dependencies remain 1.0.0;
|
|
31
|
+
the native authentication/credential boundary and wire protocol are unchanged.
|
|
32
|
+
- Supported baseline remains React Native 0.82 / New Architecture. Android
|
|
33
|
+
physical Play Integrity evidence is deferred, not inferred from a build.
|
|
34
|
+
|
|
10
35
|
## [1.0.0] - 2026-09-01
|
|
11
36
|
|
|
12
37
|
### Changed
|
package/README.md
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
`@latchway/react-native` gives iOS and Android applications one fetch-shaped API for a self-hosted Latchway gateway. The JavaScript layer never accepts an upstream AI-provider key. P-256 installation keys, DPoP signing, refresh-token storage, and platform attestation stay in the native Latchway SDKs.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Version 1.1.0 adds an optional [LangChain quickstart](docs/langchain.md), runtime
|
|
6
|
+
bootstrap and Babel helper, with standard Metro resolution. Native SDK
|
|
7
|
+
dependencies remain 1.0.0 from CocoaPods and Maven Central.
|
|
8
|
+
|
|
9
|
+
For a standalone npm-only consumer with Firebase login, LangChain weather tools,
|
|
10
|
+
streaming chat and direct-fetch Settings, see
|
|
11
|
+
[LatchwayChat](Examples/LatchwayChat/README.md). The existing `example/` remains
|
|
12
|
+
the separate workspace/conformance application.
|
|
6
13
|
|
|
7
14
|
## Requirements
|
|
8
15
|
|
package/babel.cjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Opt-in compiler compatibility for LangChain on the supported RN baseline. */
|
|
2
|
+
function withLatchwayBabel(config = {}) {
|
|
3
|
+
if (config.assumptions?.noClassCalls === false) {
|
|
4
|
+
throw new Error("LangChain on React Native requires assumptions.noClassCalls=true. Remove the conflicting assumption.");
|
|
5
|
+
}
|
|
6
|
+
const namespacePlugin = require.resolve("@babel/plugin-transform-export-namespace-from");
|
|
7
|
+
const plugins = config.plugins ?? [];
|
|
8
|
+
const hasNamespacePlugin = plugins.some((entry) => {
|
|
9
|
+
const plugin = Array.isArray(entry) ? entry[0] : entry;
|
|
10
|
+
return plugin === namespacePlugin || plugin === "@babel/plugin-transform-export-namespace-from";
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
...config,
|
|
14
|
+
// LangChain's Symbol.hasInstance reads fields initialized by super(). The
|
|
15
|
+
// Babel pre-constructor instanceof assertion runs too early. Classes must
|
|
16
|
+
// still be called with new; LangChain's own type checks remain unchanged.
|
|
17
|
+
assumptions: { ...config.assumptions, noClassCalls: true },
|
|
18
|
+
plugins: hasNamespacePlugin ? [...plugins] : [namespacePlugin, ...plugins],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { withLatchwayBabel };
|
package/babel.d.cts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface BabelConfiguration {
|
|
2
|
+
assumptions?: Record<string, boolean>;
|
|
3
|
+
plugins?: unknown[];
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
/** Preserves existing configuration and adds explicit LangChain compatibility. */
|
|
7
|
+
export function withLatchwayBabel(config?: BabelConfiguration): BabelConfiguration;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# LangChain on React Native
|
|
2
|
+
|
|
3
|
+
Use `@latchway/react-native@1.1.0` for native authenticated transport and
|
|
4
|
+
`@latchway/langchain@1.1.0` for the optional LangChain adapter. No provider key
|
|
5
|
+
belongs in the application. Secure Enclave/Keystore, App Attest/Play Integrity,
|
|
6
|
+
DPoP and refresh credentials stay native.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
The tested baseline is React Native 0.82 / React 19.1, New Architecture:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install --save-exact @latchway/react-native@1.1.0 @latchway/langchain@1.1.0 \
|
|
14
|
+
@latchway/client@1.0.0 @langchain/core@1.2.9 @langchain/openai@1.5.10 openai@7.8.0
|
|
15
|
+
cd ios && pod install && cd ..
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Keep the lockfile. LangChain is not a dependency of the base React Native SDK.
|
|
19
|
+
The native dependencies remain the public iOS/Android SDKs 1.0.0. Normal native
|
|
20
|
+
signing, Firebase/other identity and gateway platform policy setup still applies.
|
|
21
|
+
|
|
22
|
+
## Two explicit setup lines
|
|
23
|
+
|
|
24
|
+
Put the bootstrap **first** in the application entrypoint, before importing
|
|
25
|
+
React Native, Latchway, LangChain or any stream-dependent application module:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import '@latchway/react-native/polyfills';
|
|
29
|
+
import {AppRegistry} from 'react-native';
|
|
30
|
+
import App from './App';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use the optional compiler helper in `babel.config.js`:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const {withLatchwayBabel} = require('@latchway/react-native/babel');
|
|
37
|
+
module.exports = withLatchwayBabel({
|
|
38
|
+
presets: ['module:@react-native/babel-preset'],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use standard Metro; no resolver override, source alias or local SDK link is
|
|
43
|
+
needed. Rebuild the native app after installation. Clear Metro's cache if it
|
|
44
|
+
still resolves the old 1.0.0 source entry.
|
|
45
|
+
|
|
46
|
+
The bootstrap installs missing async symbols before stream dependencies,
|
|
47
|
+
incremental UTF-8 decoding, web streams, random values and abort compatibility.
|
|
48
|
+
It only replaces URL when a capability probe fails, preserves working globals,
|
|
49
|
+
and never replaces fetch or native security. The standard SDK import does not
|
|
50
|
+
initialize globals. The decoder currently uses the pinned, deprecated
|
|
51
|
+
`text-encoding@0.7.0` implementation for incremental decoding; this is a known
|
|
52
|
+
maintenance dependency, not a claim of comprehensive runtime compatibility.
|
|
53
|
+
|
|
54
|
+
The Babel helper enables `noClassCalls` and the export-namespace transform while
|
|
55
|
+
preserving the rest of your config. Classes must still be constructed with
|
|
56
|
+
`new`; LangChain's own type checks are not removed.
|
|
57
|
+
|
|
58
|
+
## Create a model
|
|
59
|
+
|
|
60
|
+
Create `latchway` using the ordinary SDK configuration and your current user's
|
|
61
|
+
identity-token callback. Then pass that client directly—no transport wrapper:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import {createLatchwayResponsesModel} from '@latchway/langchain';
|
|
65
|
+
|
|
66
|
+
const model = createLatchwayResponsesModel({
|
|
67
|
+
latchway,
|
|
68
|
+
feature: 'assistant',
|
|
69
|
+
reasoning: {effort: 'none'}, // only if your configured model supports this
|
|
70
|
+
chatOptions: {maxTokens: 1024},
|
|
71
|
+
});
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
for await (const chunk of await model.stream('Explain Latchway', {
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
})) {
|
|
76
|
+
// Render text chunks; retain original messages for usage/callback metadata.
|
|
77
|
+
}
|
|
78
|
+
// controller.abort() cancels native networking too.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The feature must route `openai_responses`, with the matching trusted accounting
|
|
82
|
+
profile, on gateway 1.0.2+. The server selects the physical model. The helper
|
|
83
|
+
does not guess reasoning capabilities from the placeholder model alias: omit
|
|
84
|
+
`reasoning` for routes that do not accept it. Stateless storage settings do not
|
|
85
|
+
guarantee retention behavior across all upstream providers.
|
|
86
|
+
|
|
87
|
+
For an `openai_chat` feature use the existing `createLatchwayChatOpenAI` instead.
|
|
88
|
+
Both factories default to no automatic framework retries. Native pre-dispatch
|
|
89
|
+
session recovery is separate; uncertain provider dispatches must not be replayed
|
|
90
|
+
casually. Explicit `chatOptions.maxRetries` is an application policy choice.
|
|
91
|
+
|
|
92
|
+
## Tools and local history
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import {bindLatchwayTools, toLatchwayReplayMessage} from '@latchway/langchain';
|
|
96
|
+
import {HumanMessage, type BaseMessage} from '@langchain/core/messages';
|
|
97
|
+
|
|
98
|
+
// weather is your ordinary LangChain tool; the SDK never executes tools for you.
|
|
99
|
+
const modelWithTools = bindLatchwayTools(model, [weather]);
|
|
100
|
+
const history: BaseMessage[] = [new HumanMessage('Weather in Singapore?')];
|
|
101
|
+
const answer = await modelWithTools.invoke(history);
|
|
102
|
+
const replay = toLatchwayReplayMessage(answer);
|
|
103
|
+
history.push(replay);
|
|
104
|
+
// Dispatch only an allowlisted tool with validated arguments. Append the
|
|
105
|
+
// returned ToolMessage, preserving the model's tool_call_id, then invoke again.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The binding helper
|
|
109
|
+
defaults to `strict:true` and `parallel_tool_calls:false`; ordinary LangChain
|
|
110
|
+
options can override these deliberately. Supply strict-compatible schemas
|
|
111
|
+
(all required fields, `additionalProperties:false`; nullable instead of optional
|
|
112
|
+
properties when necessary).
|
|
113
|
+
|
|
114
|
+
Use `toLatchwayReplayMessage` only after a successful complete response (or
|
|
115
|
+
aggregating all stream chunks). It preserves text and function-call IDs while
|
|
116
|
+
removing provider item IDs and observational metadata. It rejects non-text,
|
|
117
|
+
opaque reasoning/refusal, malformed calls and duplicate IDs. Keep the original
|
|
118
|
+
message separately for usage information. Do not save failed/partial turns as
|
|
119
|
+
history. The helper does not store history, execute tools or enforce loop limits.
|
|
120
|
+
|
|
121
|
+
Set explicit tool-call limits, timeouts, cancellation and history bounds in your
|
|
122
|
+
app. Standard LangChain `withStructuredOutput(..., {method:'jsonSchema', strict:true})`
|
|
123
|
+
is available when the gateway route/model supports the schema.
|
|
124
|
+
|
|
125
|
+
## Example and evidence
|
|
126
|
+
|
|
127
|
+
The repository's `Examples/LatchwayChat` demonstrates Firebase login, a bounded
|
|
128
|
+
streaming weather-tool loop, ephemeral chat, direct fetch, Stop and diagnostics.
|
|
129
|
+
It consumes npm packages. Its verification report distinguishes physical iOS
|
|
130
|
+
evidence from Android build-only evidence. Request framework attribution remains
|
|
131
|
+
native-owned `react-native-fetch`, including when called through LangChain.
|
|
132
|
+
|
|
133
|
+
References: [Metro exports](https://metrobundler.dev/docs/package-exports/),
|
|
134
|
+
[Babel assumptions](https://babeljs.io/docs/assumptions#noclasscalls),
|
|
135
|
+
[LangChain ChatOpenAI](https://docs.langchain.com/oss/javascript/integrations/chat/openai).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit, opt-in React Native compatibility bootstrap. Import this before
|
|
3
|
+
* LangChain or any SDK/stream dependency. The ordinary SDK entry has no global
|
|
4
|
+
* initialization side effects. Native keys and attestation are never polyfilled.
|
|
5
|
+
*/
|
|
6
|
+
import "./runtime-symbols.js";
|
|
7
|
+
import "react-native-get-random-values";
|
|
8
|
+
//# sourceMappingURL=polyfills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polyfills.d.ts","sourceRoot":"","sources":["../src/polyfills.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gCAAgC,CAAC"}
|
package/lib/polyfills.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit, opt-in React Native compatibility bootstrap. Import this before
|
|
3
|
+
* LangChain or any SDK/stream dependency. The ordinary SDK entry has no global
|
|
4
|
+
* initialization side effects. Native keys and attestation are never polyfilled.
|
|
5
|
+
*/
|
|
6
|
+
import "./runtime-symbols.js";
|
|
7
|
+
import "react-native-get-random-values";
|
|
8
|
+
import { URL as CompatibleURL, URLSearchParams as CompatibleURLSearchParams } from "react-native-url-polyfill";
|
|
9
|
+
import { ReadableStream, TransformStream, WritableStream } from "web-streams-polyfill";
|
|
10
|
+
import { TextDecoder, TextEncoder } from "text-encoding";
|
|
11
|
+
// React Native's built-in URL is present but incomplete. Replace it only when
|
|
12
|
+
// this small capability check fails, retaining complete application polyfills.
|
|
13
|
+
function hasWorkingURL() {
|
|
14
|
+
try {
|
|
15
|
+
const url = new globalThis.URL("child?q=hello%20world", "https://example.invalid/root/");
|
|
16
|
+
return url.href === "https://example.invalid/root/child?q=hello%20world" &&
|
|
17
|
+
url.searchParams.get("q") === "hello world" &&
|
|
18
|
+
new globalThis.URLSearchParams({ q: "hello world" }).get("q") === "hello world";
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!hasWorkingURL())
|
|
25
|
+
Object.assign(globalThis, { URL: CompatibleURL, URLSearchParams: CompatibleURLSearchParams });
|
|
26
|
+
if (typeof globalThis.TextEncoder === "undefined")
|
|
27
|
+
Object.assign(globalThis, { TextEncoder });
|
|
28
|
+
if (typeof globalThis.TextDecoder === "undefined")
|
|
29
|
+
Object.assign(globalThis, { TextDecoder });
|
|
30
|
+
if (typeof globalThis.ReadableStream === "undefined")
|
|
31
|
+
Object.assign(globalThis, { ReadableStream });
|
|
32
|
+
if (typeof globalThis.TransformStream === "undefined")
|
|
33
|
+
Object.assign(globalThis, { TransformStream });
|
|
34
|
+
if (typeof globalThis.WritableStream === "undefined")
|
|
35
|
+
Object.assign(globalThis, { WritableStream });
|
|
36
|
+
if (typeof AbortSignal.prototype.throwIfAborted === "undefined") {
|
|
37
|
+
Object.defineProperty(AbortSignal.prototype, "throwIfAborted", {
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value() {
|
|
41
|
+
if (!this.aborted)
|
|
42
|
+
return;
|
|
43
|
+
if (this.reason !== undefined)
|
|
44
|
+
throw this.reason;
|
|
45
|
+
const error = new Error("The operation was aborted.");
|
|
46
|
+
error.name = "AbortError";
|
|
47
|
+
throw error;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// LangChain expects a string when navigator exists. This is runtime detection,
|
|
52
|
+
// not caller-supplied trust or framework attribution in the native protocol.
|
|
53
|
+
if (typeof navigator !== "undefined" && typeof navigator.userAgent !== "string") {
|
|
54
|
+
Object.defineProperty(navigator, "userAgent", { value: "ReactNative/0.82", configurable: true });
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=polyfills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polyfills.js","sourceRoot":"","sources":["../src/polyfills.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gCAAgC,CAAC;AACxC,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,eAAe,IAAI,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEzD,8EAA8E;AAC9E,+EAA+E;AAC/E,SAAS,aAAa;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,uBAAuB,EAAE,+BAA+B,CAAC,CAAC;QACzF,OAAO,GAAG,CAAC,IAAI,KAAK,oDAAoD;YACtE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,aAAa;YAC3C,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,aAAa,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,IAAI,CAAC,aAAa,EAAE;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,eAAe,EAAE,yBAAyB,EAAE,CAAC,CAAC;AACpH,IAAI,OAAO,UAAU,CAAC,WAAW,KAAK,WAAW;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;AAC9F,IAAI,OAAO,UAAU,CAAC,WAAW,KAAK,WAAW;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;AAC9F,IAAI,OAAO,UAAU,CAAC,cAAc,KAAK,WAAW;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;AACpG,IAAI,OAAO,UAAU,CAAC,eAAe,KAAK,WAAW;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;AACtG,IAAI,OAAO,UAAU,CAAC,cAAc,KAAK,WAAW;IAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;AAEpG,IAAI,OAAO,WAAW,CAAC,SAAS,CAAC,cAAc,KAAK,WAAW,EAAE,CAAC;IAChE,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,SAAS,EAAE,gBAAgB,EAAE;QAC7D,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,IAAI;QACd,KAAK;YACH,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAO;YAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,CAAC,MAAM,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACtD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;YAC1B,MAAM,KAAK,CAAC;QACd,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,6EAA6E;AAC7E,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,OAAO,SAAS,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;IAChF,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AACnG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-symbols.d.ts","sourceRoot":"","sources":["../src/runtime-symbols.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// This module deliberately has no imports. Computed stream methods must see
|
|
2
|
+
// these symbols before any dependency is evaluated on Hermes 0.82.
|
|
3
|
+
for (const name of ["asyncIterator", "asyncDispose", "dispose"]) {
|
|
4
|
+
if (typeof Symbol[name] === "undefined") {
|
|
5
|
+
Object.defineProperty(Symbol, name, { value: Symbol.for(`Symbol.${name}`) });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=runtime-symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-symbols.js","sourceRoot":"","sources":["../src/runtime-symbols.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,mEAAmE;AACnE,KAAK,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,cAAc,EAAE,SAAS,CAAU,EAAE,CAAC;IACzE,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,WAAW,EAAE,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC"}
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@latchway/react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Hardware-backed React Native client for the Latchway AI gateway",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./lib/index.js",
|
|
8
8
|
"module": "./lib/index.js",
|
|
9
9
|
"types": "./lib/index.d.ts",
|
|
10
|
-
"react-native": "./
|
|
11
|
-
"sideEffects":
|
|
10
|
+
"react-native": "./lib/index.js",
|
|
11
|
+
"sideEffects": [
|
|
12
|
+
"./lib/polyfills.js",
|
|
13
|
+
"./lib/runtime-symbols.js"
|
|
14
|
+
],
|
|
12
15
|
"engines": {
|
|
13
16
|
"node": ">=24.19.0",
|
|
14
17
|
"pnpm": "10.15.0"
|
|
@@ -27,6 +30,8 @@
|
|
|
27
30
|
"ios",
|
|
28
31
|
"lib",
|
|
29
32
|
"src",
|
|
33
|
+
"babel.cjs",
|
|
34
|
+
"babel.d.cts",
|
|
30
35
|
"CHANGELOG.md",
|
|
31
36
|
"LICENSE",
|
|
32
37
|
"NOTICE",
|
|
@@ -40,10 +45,21 @@
|
|
|
40
45
|
"exports": {
|
|
41
46
|
".": {
|
|
42
47
|
"types": "./lib/index.d.ts",
|
|
43
|
-
"react-native": "./
|
|
48
|
+
"react-native": "./lib/index.js",
|
|
44
49
|
"import": "./lib/index.js",
|
|
45
50
|
"default": "./lib/index.js"
|
|
46
51
|
},
|
|
52
|
+
"./polyfills": {
|
|
53
|
+
"types": "./lib/polyfills.d.ts",
|
|
54
|
+
"react-native": "./lib/polyfills.js",
|
|
55
|
+
"import": "./lib/polyfills.js",
|
|
56
|
+
"default": "./lib/polyfills.js"
|
|
57
|
+
},
|
|
58
|
+
"./babel": {
|
|
59
|
+
"types": "./babel.d.cts",
|
|
60
|
+
"require": "./babel.cjs",
|
|
61
|
+
"default": "./babel.cjs"
|
|
62
|
+
},
|
|
47
63
|
"./testing": {
|
|
48
64
|
"types": "./lib/testing.d.ts",
|
|
49
65
|
"import": "./lib/testing.js",
|
|
@@ -52,7 +68,11 @@
|
|
|
52
68
|
"./package.json": "./package.json"
|
|
53
69
|
},
|
|
54
70
|
"dependencies": {
|
|
71
|
+
"@babel/plugin-transform-export-namespace-from": "7.29.7",
|
|
55
72
|
"@latchway/client": "1.0.0",
|
|
73
|
+
"react-native-get-random-values": "1.11.0",
|
|
74
|
+
"react-native-url-polyfill": "2.0.0",
|
|
75
|
+
"text-encoding": "0.7.0",
|
|
56
76
|
"web-streams-polyfill": "4.3.0"
|
|
57
77
|
},
|
|
58
78
|
"peerDependencies": {
|
|
@@ -69,6 +89,7 @@
|
|
|
69
89
|
"@react-native/gradle-plugin": "0.82.0",
|
|
70
90
|
"@types/node": "24.13.3",
|
|
71
91
|
"@types/react": "19.2.18",
|
|
92
|
+
"@types/text-encoding": "0.0.40",
|
|
72
93
|
"ai": "7.0.85",
|
|
73
94
|
"eslint": "10.9.1",
|
|
74
95
|
"globals": "17.11.0",
|
|
@@ -117,7 +138,7 @@
|
|
|
117
138
|
],
|
|
118
139
|
"scripts": {
|
|
119
140
|
"build": "node scripts/clean.mjs && tsc -p tsconfig.build.json",
|
|
120
|
-
"check": "pnpm verify:contracts && pnpm verify:compatibility && pnpm lint && pnpm typecheck && pnpm test && pnpm codegen:check && pnpm build && pnpm example:check && pnpm example:bundle:check && pnpm native:boundary",
|
|
141
|
+
"check": "pnpm verify:contracts && pnpm verify:compatibility && pnpm lint && pnpm typecheck && pnpm test:runtime && pnpm test && pnpm codegen:check && pnpm build && pnpm example:check && pnpm example:bundle:check && pnpm native:boundary",
|
|
121
142
|
"codegen:check": "node scripts/verify-codegen.mjs",
|
|
122
143
|
"docs:bundle": "python3 scripts/build_docs_bundle.py",
|
|
123
144
|
"example:check": "tsc -p tsconfig.example.json --noEmit",
|
|
@@ -134,6 +155,7 @@
|
|
|
134
155
|
"consumer:check": "node scripts/verify-consumer.mjs",
|
|
135
156
|
"test": "vitest run && node --test scripts/test-npm-release-evidence.mjs scripts/test-android-release-evidence.mjs scripts/test-ci-lock-output.mjs scripts/test-package-archive.mjs && python3 -m unittest scripts/test_build_docs_bundle.py scripts/test_maintainer_release.py scripts/test_public_core_release.py && python3 scripts/test-dependency-vulnerability-scan.py",
|
|
136
157
|
"test:watch": "vitest",
|
|
158
|
+
"test:runtime": "node --test scripts/test-runtime.mjs",
|
|
137
159
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
138
160
|
"verify:bundle": "node scripts/verify-contract-bundle.mjs",
|
|
139
161
|
"verify:compatibility": "node scripts/verify-compatibility.mjs",
|
package/src/polyfills.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit, opt-in React Native compatibility bootstrap. Import this before
|
|
3
|
+
* LangChain or any SDK/stream dependency. The ordinary SDK entry has no global
|
|
4
|
+
* initialization side effects. Native keys and attestation are never polyfilled.
|
|
5
|
+
*/
|
|
6
|
+
import "./runtime-symbols.js";
|
|
7
|
+
import "react-native-get-random-values";
|
|
8
|
+
import { URL as CompatibleURL, URLSearchParams as CompatibleURLSearchParams } from "react-native-url-polyfill";
|
|
9
|
+
import { ReadableStream, TransformStream, WritableStream } from "web-streams-polyfill";
|
|
10
|
+
import { TextDecoder, TextEncoder } from "text-encoding";
|
|
11
|
+
|
|
12
|
+
// React Native's built-in URL is present but incomplete. Replace it only when
|
|
13
|
+
// this small capability check fails, retaining complete application polyfills.
|
|
14
|
+
function hasWorkingURL(): boolean {
|
|
15
|
+
try {
|
|
16
|
+
const url = new globalThis.URL("child?q=hello%20world", "https://example.invalid/root/");
|
|
17
|
+
return url.href === "https://example.invalid/root/child?q=hello%20world" &&
|
|
18
|
+
url.searchParams.get("q") === "hello world" &&
|
|
19
|
+
new globalThis.URLSearchParams({ q: "hello world" }).get("q") === "hello world";
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!hasWorkingURL()) Object.assign(globalThis, { URL: CompatibleURL, URLSearchParams: CompatibleURLSearchParams });
|
|
26
|
+
if (typeof globalThis.TextEncoder === "undefined") Object.assign(globalThis, { TextEncoder });
|
|
27
|
+
if (typeof globalThis.TextDecoder === "undefined") Object.assign(globalThis, { TextDecoder });
|
|
28
|
+
if (typeof globalThis.ReadableStream === "undefined") Object.assign(globalThis, { ReadableStream });
|
|
29
|
+
if (typeof globalThis.TransformStream === "undefined") Object.assign(globalThis, { TransformStream });
|
|
30
|
+
if (typeof globalThis.WritableStream === "undefined") Object.assign(globalThis, { WritableStream });
|
|
31
|
+
|
|
32
|
+
if (typeof AbortSignal.prototype.throwIfAborted === "undefined") {
|
|
33
|
+
Object.defineProperty(AbortSignal.prototype, "throwIfAborted", {
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value(this: AbortSignal): void {
|
|
37
|
+
if (!this.aborted) return;
|
|
38
|
+
if (this.reason !== undefined) throw this.reason;
|
|
39
|
+
const error = new Error("The operation was aborted.");
|
|
40
|
+
error.name = "AbortError";
|
|
41
|
+
throw error;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// LangChain expects a string when navigator exists. This is runtime detection,
|
|
47
|
+
// not caller-supplied trust or framework attribution in the native protocol.
|
|
48
|
+
if (typeof navigator !== "undefined" && typeof navigator.userAgent !== "string") {
|
|
49
|
+
Object.defineProperty(navigator, "userAgent", { value: "ReactNative/0.82", configurable: true });
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// This module deliberately has no imports. Computed stream methods must see
|
|
2
|
+
// these symbols before any dependency is evaluated on Hermes 0.82.
|
|
3
|
+
for (const name of ["asyncIterator", "asyncDispose", "dispose"] as const) {
|
|
4
|
+
if (typeof Symbol[name] === "undefined") {
|
|
5
|
+
Object.defineProperty(Symbol, name, { value: Symbol.for(`Symbol.${name}`) });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {};
|
package/src/version.ts
CHANGED