@yahoo/uds-create-sidecar 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +14145 -0
- package/dist/registry.d.ts +36 -0
- package/dist/registry.js +38 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# UDS Create Sidecar
|
|
2
|
+
|
|
3
|
+
`@yahoo/uds-create-sidecar` lets an Expo development build render Create
|
|
4
|
+
previews inside the customer's own app. The preview therefore uses the same
|
|
5
|
+
native packages, configuration, and package versions as that app.
|
|
6
|
+
|
|
7
|
+
The package contains the existing Sidecar pairing UI, preview-room client,
|
|
8
|
+
authenticated and integrity-checked bundle loader, and renderer host. The only
|
|
9
|
+
required component prop is an explicit module registry:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Create browser ── signed room token ──> UDSCreateSidecar
|
|
13
|
+
│
|
|
14
|
+
Customer app ── exact modules + versions ──┘
|
|
15
|
+
│
|
|
16
|
+
└──> real app dependencies
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The standalone app in `apps/create-sidecar` consumes this same package and is
|
|
20
|
+
the reference implementation.
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
This first package targets Expo SDK 57 development builds with React Native
|
|
25
|
+
0.86. It is not supported in Expo Go because registered packages may contain
|
|
26
|
+
native code.
|
|
27
|
+
|
|
28
|
+
Install the host and the shared JSON renderer as development dependencies:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bun add --dev --exact @yahoo/uds-create-sidecar @json-render/react
|
|
32
|
+
bunx expo install react-native-safe-area-context
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Install and configure every native package used by the design system in the
|
|
36
|
+
usual way. Sidecar does not replace that package's native setup.
|
|
37
|
+
`UDSCreateSidecar` must be rendered below the app's `SafeAreaProvider`.
|
|
38
|
+
|
|
39
|
+
### What the package expects from the app
|
|
40
|
+
|
|
41
|
+
Every package Sidecar needs from the app is a declared peer dependency, so a
|
|
42
|
+
missing one is reported at install time:
|
|
43
|
+
|
|
44
|
+
- `react`, `react-native`, `react-native-safe-area-context`, and `expo` are the
|
|
45
|
+
app's own copies. Sidecar renders its pairing UI with them.
|
|
46
|
+
- `@json-render/react` is the renderer every Create preview bundle imports.
|
|
47
|
+
Sidecar never imports it itself, but the registry requires it as a module,
|
|
48
|
+
so the app must install it and register it.
|
|
49
|
+
|
|
50
|
+
### What the package carries
|
|
51
|
+
|
|
52
|
+
The preview-room client is built on Yjs and the Hocuspocus provider. Both are
|
|
53
|
+
compiled into the package, together with their `lib0` and `y-protocols`
|
|
54
|
+
internals, so the app does not install them and the app's own versions never
|
|
55
|
+
matter. Compiling them in also lets the package replace `lib0`'s Web Crypto
|
|
56
|
+
binding with a small shim over `@noble/hashes`, the package's one runtime
|
|
57
|
+
dependency, so React Native needs no Web Crypto polyfill. Yjs uses those values
|
|
58
|
+
only for client and document identifiers.
|
|
59
|
+
|
|
60
|
+
An app that also uses Yjs sees one console error the first time the Sidecar
|
|
61
|
+
screen loads, `Yjs was already imported`, shown as a LogBox error in a
|
|
62
|
+
development build. It is Yjs noticing a second copy of itself. Sidecar's
|
|
63
|
+
documents never cross into the app's Yjs, so the two copies never meet and the
|
|
64
|
+
message can be dismissed.
|
|
65
|
+
|
|
66
|
+
## 1. Define the host registry
|
|
67
|
+
|
|
68
|
+
Create one module that statically imports everything a downloaded preview may
|
|
69
|
+
use. Metro must see these imports when it builds the app; downloaded JavaScript
|
|
70
|
+
cannot discover or import an arbitrary installed native package later.
|
|
71
|
+
|
|
72
|
+
This example exposes `react-native-maps`, a package that is intentionally not
|
|
73
|
+
built into the standalone Sidecar:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// src/create-sidecar-registry.ts
|
|
77
|
+
import * as JsonRenderReact from "@json-render/react";
|
|
78
|
+
import { createUDSCreateSidecarRegistry } from "@yahoo/uds-create-sidecar/registry";
|
|
79
|
+
import * as React from "react";
|
|
80
|
+
import * as ReactJsxRuntime from "react/jsx-runtime";
|
|
81
|
+
import * as ReactNative from "react-native";
|
|
82
|
+
import * as ReactNativeMaps from "react-native-maps";
|
|
83
|
+
import appPackage from "../package.json";
|
|
84
|
+
|
|
85
|
+
export const createSidecarRegistry = createUDSCreateSidecarRegistry({
|
|
86
|
+
hostName: "Acme mobile app",
|
|
87
|
+
hostVersion: appPackage.version,
|
|
88
|
+
modules: {
|
|
89
|
+
"@json-render/react": JsonRenderReact,
|
|
90
|
+
react: React,
|
|
91
|
+
"react/jsx-runtime": ReactJsxRuntime,
|
|
92
|
+
"react-native": ReactNative,
|
|
93
|
+
"react-native-maps": ReactNativeMaps,
|
|
94
|
+
},
|
|
95
|
+
packageVersions: {
|
|
96
|
+
...appPackage.dependencies,
|
|
97
|
+
...appPackage.devDependencies,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The versions for every exposed package must be exact strings such as `1.2.3`,
|
|
103
|
+
not ranges such as `^1.2.3`. Pin those dependencies in `package.json` or provide
|
|
104
|
+
exact versions from the app's lockfile. The registry rejects a missing or
|
|
105
|
+
non-exact version immediately.
|
|
106
|
+
|
|
107
|
+
Subpath imports must be registered separately, but they use the package root's
|
|
108
|
+
version. For example, registering `react/jsx-dev-runtime` still uses the
|
|
109
|
+
`react` version.
|
|
110
|
+
|
|
111
|
+
### How Create checks the registry
|
|
112
|
+
|
|
113
|
+
Each preview build lists the packages it imports, the version the design system
|
|
114
|
+
declares for each, and the exact import paths it reads. The connected host
|
|
115
|
+
advertises the registry, and Create and the phone run the same comparison:
|
|
116
|
+
|
|
117
|
+
- A required package or import path that is not registered is missing.
|
|
118
|
+
- A registered version whose major or minor differs from the required version
|
|
119
|
+
is a mismatch. Patch versions may differ.
|
|
120
|
+
|
|
121
|
+
While the phone is connected, Create's phone controls name the packages the
|
|
122
|
+
host cannot provide. The phone repeats the check before evaluating a bundle and
|
|
123
|
+
refuses with an error naming the package and, for a mismatch, both versions.
|
|
124
|
+
Either side can move: install the version the design system declares, or update
|
|
125
|
+
the version the design system declares for that package.
|
|
126
|
+
|
|
127
|
+
## 2. Add a development-only route
|
|
128
|
+
|
|
129
|
+
The app, rather than the component, owns the production gate. This lets the
|
|
130
|
+
standalone Sidecar remain a normal release build while a customer app can make
|
|
131
|
+
the route impossible to activate in production.
|
|
132
|
+
|
|
133
|
+
For Expo Router, guard the package load as well as the rendered route:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
// app/create-preview.tsx
|
|
137
|
+
import { Redirect } from "expo-router";
|
|
138
|
+
import { createSidecarRegistry } from "../src/create-sidecar-registry";
|
|
139
|
+
|
|
140
|
+
declare const require: (
|
|
141
|
+
id: "@yahoo/uds-create-sidecar",
|
|
142
|
+
) => typeof import("@yahoo/uds-create-sidecar");
|
|
143
|
+
|
|
144
|
+
export default function CreatePreviewRoute() {
|
|
145
|
+
if (!__DEV__) return <Redirect href="/" />;
|
|
146
|
+
|
|
147
|
+
const { UDSCreateSidecar } = require("@yahoo/uds-create-sidecar");
|
|
148
|
+
return <UDSCreateSidecar registry={createSidecarRegistry} />;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Use the equivalent compile-time development constant and production fallback
|
|
153
|
+
with another router. Do not gate only a navigation link: the route itself must
|
|
154
|
+
return before loading or rendering Sidecar. The package test compiles this
|
|
155
|
+
pattern with `__DEV__` set to false and verifies that the Sidecar reference is
|
|
156
|
+
removed from the route's execution path.
|
|
157
|
+
|
|
158
|
+
The two entry points are split for this gate. `@yahoo/uds-create-sidecar/registry`
|
|
159
|
+
holds only the registry builder, imports nothing, and is safe to import at
|
|
160
|
+
module top level in every build, including production. The root entry holds the
|
|
161
|
+
pairing UI, the preview-room client, and the compiled-in Yjs, so it stays behind
|
|
162
|
+
the compile-time check.
|
|
163
|
+
|
|
164
|
+
## 3. Pair with Create
|
|
165
|
+
|
|
166
|
+
Open the development route. `UDSCreateSidecar` uses production Create by
|
|
167
|
+
default and shows the same origin chooser, short-code pairing, status UI,
|
|
168
|
+
disconnect behavior, and full-screen design preview as the standalone app. A
|
|
169
|
+
custom origin remains selected while the component is mounted; a new launch
|
|
170
|
+
returns to production.
|
|
171
|
+
|
|
172
|
+
It also accepts an existing Sidecar deep link when the host app routes that URL
|
|
173
|
+
to the component; short-code pairing is the portable default for an embedded
|
|
174
|
+
host with its own URL scheme.
|
|
175
|
+
|
|
176
|
+
When the host router mounts this screen from a deep link, pass the route values
|
|
177
|
+
to the component. This avoids relying on a link event that may have fired before
|
|
178
|
+
the route mounted:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import type { UDSCreateSidecarDeepLink } from "@yahoo/uds-create-sidecar";
|
|
182
|
+
import { useLocalSearchParams } from "expo-router";
|
|
183
|
+
|
|
184
|
+
const params = useLocalSearchParams<UDSCreateSidecarDeepLink>();
|
|
185
|
+
const deepLink =
|
|
186
|
+
params.origin && params.room && params.token && params.ws
|
|
187
|
+
? {
|
|
188
|
+
origin: params.origin,
|
|
189
|
+
room: params.room,
|
|
190
|
+
token: params.token,
|
|
191
|
+
ws: params.ws,
|
|
192
|
+
}
|
|
193
|
+
: undefined;
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<UDSCreateSidecar registry={createSidecarRegistry} deepLink={deepLink} />
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Create's QR code uses the `uds-create-sidecar` scheme. An embedded app must
|
|
201
|
+
route that scheme to its development-only preview screen to use QR pairing.
|
|
202
|
+
Apps that do not own that route should use the short pairing code.
|
|
203
|
+
|
|
204
|
+
In Create, open **Connect phone**, choose **Connect Sidecar**, and enter the
|
|
205
|
+
code from the app. The embedded host advertises the registry's exact package
|
|
206
|
+
versions through the existing preview protocol; no protocol or token changes
|
|
207
|
+
are required.
|
|
208
|
+
|
|
209
|
+
## Security boundary
|
|
210
|
+
|
|
211
|
+
The host downloads a bundle only from the selected Create origin, sends the
|
|
212
|
+
room-scoped bearer token, limits the response to 5 MiB, and verifies the
|
|
213
|
+
manifest's SHA-256 hash before evaluation. Module resolution is limited to the
|
|
214
|
+
explicit registry.
|
|
215
|
+
|
|
216
|
+
This is not a JavaScript sandbox. An authorized Create bundle runs inside the
|
|
217
|
+
development app's JavaScript process and can use normal JavaScript globals in
|
|
218
|
+
addition to registered modules. Expose only modules intended for preview, use
|
|
219
|
+
only a trusted Create deployment, and keep the route hard-disabled in
|
|
220
|
+
production.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UDSCreateSidecarModules, UDSCreateSidecarRegistry, UDSCreateSidecarRegistryOptions, createUDSCreateSidecarRegistry } from "./registry.js";
|
|
2
|
+
import * as _$react from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/deep-link.d.ts
|
|
5
|
+
interface UDSCreateSidecarDeepLink {
|
|
6
|
+
readonly origin: string;
|
|
7
|
+
readonly room: string;
|
|
8
|
+
readonly token: string;
|
|
9
|
+
readonly ws: string;
|
|
10
|
+
}
|
|
11
|
+
declare function parseUDSCreateSidecarDeepLink(url: string): UDSCreateSidecarDeepLink | undefined;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/UDSCreateSidecar.d.ts
|
|
14
|
+
interface UDSCreateSidecarProps {
|
|
15
|
+
readonly registry: UDSCreateSidecarRegistry;
|
|
16
|
+
/** Pairing values read by the host router from its current deep link. */
|
|
17
|
+
readonly deepLink?: UDSCreateSidecarDeepLink;
|
|
18
|
+
}
|
|
19
|
+
declare function UDSCreateSidecar({
|
|
20
|
+
deepLink,
|
|
21
|
+
registry
|
|
22
|
+
}: UDSCreateSidecarProps): _$react.JSX.Element;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { UDSCreateSidecar, type UDSCreateSidecarDeepLink, type UDSCreateSidecarModules, type UDSCreateSidecarProps, type UDSCreateSidecarRegistry, type UDSCreateSidecarRegistryOptions, createUDSCreateSidecarRegistry, parseUDSCreateSidecarDeepLink };
|