@gusnips/native 0.0.0-stage → 0.1.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 +311 -2
- package/dist/device-locale.d.ts +15 -0
- package/dist/device-locale.d.ts.map +1 -0
- package/dist/device-locale.js +27 -0
- package/dist/device-locale.js.map +1 -0
- package/dist/i18n.d.ts +34 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +63 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +18 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +35 -0
- package/dist/network.js.map +1 -0
- package/dist/node/bin/gusnips-native-css.d.ts +3 -0
- package/dist/node/bin/gusnips-native-css.d.ts.map +1 -0
- package/dist/node/bin/gusnips-native-css.js +75 -0
- package/dist/node/bin/gusnips-native-css.js.map +1 -0
- package/dist/node/css.d.ts +50 -0
- package/dist/node/css.d.ts.map +1 -0
- package/dist/node/css.js +195 -0
- package/dist/node/css.js.map +1 -0
- package/dist/node/metro.d.ts +51 -0
- package/dist/node/metro.d.ts.map +1 -0
- package/dist/node/metro.js +100 -0
- package/dist/node/metro.js.map +1 -0
- package/dist/query.d.ts +13 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +37 -0
- package/dist/query.js.map +1 -0
- package/dist/sse.d.ts +47 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +135 -0
- package/dist/sse.js.map +1 -0
- package/dist/supabase.d.ts +18 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +29 -0
- package/dist/supabase.js.map +1 -0
- package/dist/theme.d.ts +25 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +99 -0
- package/dist/theme.js.map +1 -0
- package/package.json +150 -4
- package/src/device-locale.test.ts +33 -0
- package/src/device-locale.ts +28 -0
- package/src/i18n.ts +91 -0
- package/src/index.ts +18 -0
- package/src/network.ts +37 -0
- package/src/node/bin/gusnips-native-css.ts +84 -0
- package/src/node/css.test.ts +19 -0
- package/src/node/css.ts +259 -0
- package/src/node/metro.test.ts +66 -0
- package/src/node/metro.ts +143 -0
- package/src/query.ts +41 -0
- package/src/sse.test.ts +195 -0
- package/src/sse.ts +174 -0
- package/src/supabase.ts +29 -0
- package/src/theme.test.ts +92 -0
- package/src/theme.ts +132 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* gusnips-native-css: `buildNativeCss` from the command line, for an app's `start` script.
|
|
4
|
+
*
|
|
5
|
+
* Scoped like every bin in this repo: a bare name belongs to whoever registers it on npm, and
|
|
6
|
+
* `bunx` would download and run theirs.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { parseArgs } from "node:util";
|
|
11
|
+
import { buildNativeCss } from "../css.js";
|
|
12
|
+
const HELP = `gusnips-native-css: compile global.css for React Native, before Metro starts.
|
|
13
|
+
|
|
14
|
+
Usage: gusnips-native-css [options]
|
|
15
|
+
|
|
16
|
+
--root <dir> the app's folder (default: the current folder)
|
|
17
|
+
--input <file> the Tailwind entry, from the app's folder (default: global.css)
|
|
18
|
+
--out <dir> where stylesheet.ts goes (default: src/generated)
|
|
19
|
+
--palette <file> a JSON file naming colours for code that cannot take a className:
|
|
20
|
+
{ "text": { "light": "text-ink", "dark": "dark:text-ink-dark" } }
|
|
21
|
+
writes colors.ts beside stylesheet.ts
|
|
22
|
+
--rem <px> pixels per rem (default: 16, as on the web)
|
|
23
|
+
-h, --help show this
|
|
24
|
+
|
|
25
|
+
Run it before Metro, in the same script: "start": "gusnips-native-css && react-native start"
|
|
26
|
+
`;
|
|
27
|
+
async function main() {
|
|
28
|
+
const { values } = parseArgs({
|
|
29
|
+
options: {
|
|
30
|
+
root: { type: "string" },
|
|
31
|
+
input: { type: "string" },
|
|
32
|
+
out: { type: "string" },
|
|
33
|
+
palette: { type: "string" },
|
|
34
|
+
rem: { type: "string" },
|
|
35
|
+
help: { type: "boolean", short: "h" },
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (values.help) {
|
|
39
|
+
process.stdout.write(HELP);
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
const root = resolve(values.root ?? ".");
|
|
43
|
+
const result = await buildNativeCss({
|
|
44
|
+
root,
|
|
45
|
+
input: values.input,
|
|
46
|
+
outDir: values.out,
|
|
47
|
+
rem: values.rem === undefined ? undefined : Number(values.rem),
|
|
48
|
+
palette: values.palette === undefined ? undefined : await readPalette(resolve(values.palette)),
|
|
49
|
+
});
|
|
50
|
+
console.log(`native css: ${result.classes} classes → ${result.files.join(", ")}`);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
/** The palette file is the app's own, but a typo in it should read as one, not as a crash. */
|
|
54
|
+
async function readPalette(file) {
|
|
55
|
+
const parsed = JSON.parse(await readFile(file, "utf-8"));
|
|
56
|
+
const palette = {};
|
|
57
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
58
|
+
throw new Error(`${file} must hold an object of colour names.`);
|
|
59
|
+
for (const [name, entry] of Object.entries(parsed)) {
|
|
60
|
+
const light = entry?.light;
|
|
61
|
+
const dark = entry?.dark;
|
|
62
|
+
if (typeof light !== "string" || typeof dark !== "string")
|
|
63
|
+
throw new Error(`${file}: "${name}" needs a "light" and a "dark" class, such as ` +
|
|
64
|
+
'{ "light": "text-ink", "dark": "dark:text-ink-dark" }.');
|
|
65
|
+
palette[name] = { light, dark };
|
|
66
|
+
}
|
|
67
|
+
return palette;
|
|
68
|
+
}
|
|
69
|
+
main().then((code) => {
|
|
70
|
+
process.exitCode = code;
|
|
71
|
+
}, (error) => {
|
|
72
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=gusnips-native-css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gusnips-native-css.js","sourceRoot":"","sources":["../../../src/node/bin/gusnips-native-css.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** A colour code needs where it cannot take a `className`: one utility class per scheme. */
|
|
2
|
+
export interface PaletteEntry {
|
|
3
|
+
/** A class that paints this colour in light mode, such as `"bg-surface"`. */
|
|
4
|
+
light: string;
|
|
5
|
+
/** The same in dark mode, such as `"dark:bg-dark-surface"`. */
|
|
6
|
+
dark: string;
|
|
7
|
+
}
|
|
8
|
+
export interface NativeCssOptions {
|
|
9
|
+
/** The app's folder. The other paths are read from it. Default: the current folder. */
|
|
10
|
+
root?: string;
|
|
11
|
+
/** The Tailwind entry. Default: `global.css`. */
|
|
12
|
+
input?: string;
|
|
13
|
+
/** Where `stylesheet.ts` (and `colors.ts`) are written. Default: `src/generated`. */
|
|
14
|
+
outDir?: string;
|
|
15
|
+
/**
|
|
16
|
+
* How many pixels one `rem` is. Default: 16, the web's.
|
|
17
|
+
*
|
|
18
|
+
* react-native-css's own default is 14, React Native's root font size, and that silently shrinks
|
|
19
|
+
* every rem-based size by an eighth: body text from 14 to 12.25 px and `h-12` from 48 to 42 px,
|
|
20
|
+
* against tokens written for the web. A `:root { font-size }` in the CSS does not fix it
|
|
21
|
+
* reliably, so the number is passed to the compiler instead.
|
|
22
|
+
*/
|
|
23
|
+
rem?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Hex colours for what cannot take a `className`: an icon's `color`, the navigation theme, the
|
|
26
|
+
* status bar. Each name maps to a class in each scheme, and `colors.ts` gets the hex those classes
|
|
27
|
+
* compile to. Read from the stylesheet itself, so a change of tokens cannot restyle the classes
|
|
28
|
+
* and leave the icons in the old colours, which is what a hand-copied list of hex values did.
|
|
29
|
+
*/
|
|
30
|
+
palette?: Record<string, PaletteEntry>;
|
|
31
|
+
}
|
|
32
|
+
export interface NativeCssResult {
|
|
33
|
+
/** How many classes the stylesheet holds. */
|
|
34
|
+
classes: number;
|
|
35
|
+
/** The files written, as absolute paths. */
|
|
36
|
+
files: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* ```ts
|
|
40
|
+
* await buildNativeCss(); // global.css → src/generated/stylesheet.ts
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildNativeCss(options?: NativeCssOptions): Promise<NativeCssResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Whether this lightningcss breaks react-native-css 3.0.7's compiler. 1.30.2 is the release
|
|
46
|
+
* react-native-css itself names, and an adopter's app then measured 1.32.0 and 1.33.0 failing the
|
|
47
|
+
* same way, so every release after 1.30.1 counts as broken until one is measured working.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isBrokenLightningcss(version: string): boolean;
|
|
50
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"AA8BA,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2C7F;AAuDD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK7D"}
|
package/dist/node/css.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind classes for a bare React Native app, built before Metro starts.
|
|
3
|
+
*
|
|
4
|
+
* NativeWind v5 and react-native-css resolve `className` at runtime against a compiled stylesheet.
|
|
5
|
+
* Their own way of compiling it is a Metro transformer that hands the CSS step to
|
|
6
|
+
* `@expo/metro-config`'s worker, which needs the `expo` package and its native modules. A bare app
|
|
7
|
+
* has neither, so this runs the same two Expo-free stages itself:
|
|
8
|
+
*
|
|
9
|
+
* 1. the Tailwind v4 CLI compiles the app's `global.css` to plain CSS, finding the classes in the
|
|
10
|
+
* app's own files (Tailwind v4 has no `--content` flag; a file outside the app folder needs an
|
|
11
|
+
* `@source` line in `global.css`);
|
|
12
|
+
* 2. react-native-css's compiler turns that CSS into the stylesheet its runtime reads.
|
|
13
|
+
*
|
|
14
|
+
* The output is a TypeScript module the app injects at launch with `StyleCollection.inject`.
|
|
15
|
+
* Editing a `className` hot-reloads as usual; only a change to the CSS or the tokens needs a rebuild,
|
|
16
|
+
* which is why the build runs in the `start`, `ios` and `android` scripts, before Metro.
|
|
17
|
+
*
|
|
18
|
+
* Both tools are resolved from the APP's folder, not from this package: the stylesheet has to be in
|
|
19
|
+
* the format of the react-native-css the bundle will run, and `global.css` imports `tailwindcss`
|
|
20
|
+
* from where the app installed it.
|
|
21
|
+
*/
|
|
22
|
+
import { spawnSync } from "node:child_process";
|
|
23
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
24
|
+
import { createRequire } from "node:module";
|
|
25
|
+
import { tmpdir } from "node:os";
|
|
26
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
27
|
+
/**
|
|
28
|
+
* ```ts
|
|
29
|
+
* await buildNativeCss(); // global.css → src/generated/stylesheet.ts
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export async function buildNativeCss(options = {}) {
|
|
33
|
+
const root = resolve(options.root ?? ".");
|
|
34
|
+
const input = resolve(root, options.input ?? "global.css");
|
|
35
|
+
const outDir = resolve(root, options.outDir ?? "src/generated");
|
|
36
|
+
const rem = options.rem ?? 16;
|
|
37
|
+
if (!(rem > 0))
|
|
38
|
+
throw new Error(`buildNativeCss: rem must be a positive number, not ${rem}.`);
|
|
39
|
+
const fromApp = createRequire(join(root, "package.json"));
|
|
40
|
+
// Before Tailwind, so a missing react-native-css fails in a moment rather than after a compile.
|
|
41
|
+
const compiler = loadCompiler(fromApp);
|
|
42
|
+
const css = await runTailwind(fromApp, root, input);
|
|
43
|
+
const stylesheet = compiler.compile(css, { inlineRem: rem }).stylesheet();
|
|
44
|
+
dropUnmappable(stylesheet);
|
|
45
|
+
await mkdir(outDir, { recursive: true });
|
|
46
|
+
const banner = `// Generated by gusnips-native-css from ${relative(root, input)}. Do not edit: run it again.\n`;
|
|
47
|
+
const stylesheetFile = join(outDir, "stylesheet.ts");
|
|
48
|
+
await writeFile(stylesheetFile, `${banner}\nimport type { ReactNativeCssStyleSheet } from "react-native-css/compiler";\n\n` +
|
|
49
|
+
`export const stylesheet: ReactNativeCssStyleSheet = ${JSON.stringify(stylesheet)};\n`);
|
|
50
|
+
const files = [stylesheetFile];
|
|
51
|
+
const { palette } = options;
|
|
52
|
+
if (palette) {
|
|
53
|
+
const scheme = (mode) => Object.fromEntries(Object.entries(palette).map(([name, entry]) => [
|
|
54
|
+
name,
|
|
55
|
+
colorOf(stylesheet, entry[mode], root),
|
|
56
|
+
]));
|
|
57
|
+
const colors = { light: scheme("light"), dark: scheme("dark") };
|
|
58
|
+
const colorsFile = join(outDir, "colors.ts");
|
|
59
|
+
await writeFile(colorsFile, `${banner}\nexport const COLORS = ${JSON.stringify(colors, null, 2)};\n`);
|
|
60
|
+
files.push(colorsFile);
|
|
61
|
+
}
|
|
62
|
+
return { classes: Array.isArray(stylesheet.s) ? stylesheet.s.length : 0, files };
|
|
63
|
+
}
|
|
64
|
+
async function runTailwind(fromApp, root, input) {
|
|
65
|
+
const cli = tailwindCli(fromApp);
|
|
66
|
+
// Out of the app's tree, so building reads the app and writes only `outDir`. Kept when the
|
|
67
|
+
// compile fails, because the half-written CSS is the first thing worth reading.
|
|
68
|
+
const work = await mkdtemp(join(tmpdir(), "gusnips-native-css-"));
|
|
69
|
+
const out = join(work, "tailwind.css");
|
|
70
|
+
const run = spawnSync(process.execPath, [cli, "-i", input, "-o", out], {
|
|
71
|
+
cwd: root,
|
|
72
|
+
encoding: "utf-8",
|
|
73
|
+
// The compile takes seconds. A hung CLI with no deadline holds `bun run start` forever.
|
|
74
|
+
timeout: 120_000,
|
|
75
|
+
});
|
|
76
|
+
if (run.error !== undefined)
|
|
77
|
+
throw new Error(`tailwindcss did not finish: ${run.error.message}`);
|
|
78
|
+
if (run.status !== 0)
|
|
79
|
+
throw new Error(`tailwindcss could not compile ${input}` +
|
|
80
|
+
`${run.stderr ? `:\n${run.stderr.slice(0, 1000)}` : ""}\n(work folder kept at ${work})`);
|
|
81
|
+
const css = await readFile(out, "utf-8");
|
|
82
|
+
await rm(work, { recursive: true, force: true });
|
|
83
|
+
return css;
|
|
84
|
+
}
|
|
85
|
+
function tailwindCli(fromApp) {
|
|
86
|
+
let manifestPath;
|
|
87
|
+
try {
|
|
88
|
+
manifestPath = fromApp.resolve("@tailwindcss/cli/package.json");
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
throw new Error(missing("@tailwindcss/cli"));
|
|
92
|
+
}
|
|
93
|
+
const manifest = fromApp(manifestPath);
|
|
94
|
+
const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.["tailwindcss"];
|
|
95
|
+
if (!bin)
|
|
96
|
+
throw new Error(`@tailwindcss/cli at ${manifestPath} names no tailwindcss bin.`);
|
|
97
|
+
return join(dirname(manifestPath), bin);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The CommonJS build, on purpose: react-native-css's ES module build imports `debug` by a named
|
|
101
|
+
* export Node cannot see in a CommonJS package, so it does not load under plain Node (bun forgives
|
|
102
|
+
* it). `require` loads the build that works under both.
|
|
103
|
+
*/
|
|
104
|
+
function loadCompiler(fromApp) {
|
|
105
|
+
let compiler;
|
|
106
|
+
try {
|
|
107
|
+
compiler = fromApp("react-native-css/compiler");
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (isNotFound(error))
|
|
111
|
+
throw new Error(missing("react-native-css"));
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
refuseBrokenLightningcss(fromApp);
|
|
115
|
+
return compiler;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether this lightningcss breaks react-native-css 3.0.7's compiler. 1.30.2 is the release
|
|
119
|
+
* react-native-css itself names, and an adopter's app then measured 1.32.0 and 1.33.0 failing the
|
|
120
|
+
* same way, so every release after 1.30.1 counts as broken until one is measured working.
|
|
121
|
+
*/
|
|
122
|
+
export function isBrokenLightningcss(version) {
|
|
123
|
+
const [major = 0, minor = 0, patch = 0] = version.split(/[.+-]/, 3).map(Number);
|
|
124
|
+
if (major !== 1)
|
|
125
|
+
return major > 1;
|
|
126
|
+
if (minor !== 30)
|
|
127
|
+
return minor > 30;
|
|
128
|
+
return patch > 1;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* react-native-css checks for a broken lightningcss and means to throw, but the check sits inside a
|
|
132
|
+
* `try` whose `catch` is empty, so the throw is swallowed and the build goes on. This asks the same
|
|
133
|
+
* copy its loader picks: the one under `@expo/metro-config` first (a peer of react-native-css, so
|
|
134
|
+
* bun installs it), then its own.
|
|
135
|
+
*
|
|
136
|
+
* That order is also why a devDependency on lightningcss does not fix it. Pin it with `overrides`
|
|
137
|
+
* in the root `package.json`, which reaches every copy.
|
|
138
|
+
*/
|
|
139
|
+
function refuseBrokenLightningcss(fromApp) {
|
|
140
|
+
const fromCss = createRequire(fromApp.resolve("react-native-css/package.json"));
|
|
141
|
+
const candidates = [];
|
|
142
|
+
try {
|
|
143
|
+
candidates.push(createRequire(fromCss.resolve("@expo/metro-config/package.json")));
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// No expo config beside it: react-native-css loads its own lightningcss.
|
|
147
|
+
}
|
|
148
|
+
candidates.push(fromCss);
|
|
149
|
+
for (const from of candidates) {
|
|
150
|
+
let version;
|
|
151
|
+
try {
|
|
152
|
+
const manifest = from("lightningcss/package.json");
|
|
153
|
+
version = manifest.version;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (isBrokenLightningcss(version))
|
|
159
|
+
throw new Error(`lightningcss ${version} breaks react-native-css's compiler (every release after 1.30.1 ` +
|
|
160
|
+
'measured so far does). Pin it with "overrides": { "lightningcss": "1.30.1" } in the root ' +
|
|
161
|
+
"package.json, then run bun install.");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Tailwind finds class names by reading source as text, so a React Native style key such as
|
|
167
|
+
* `transform` (an Animated style needs one) becomes a candidate. `transform` IS a Tailwind utility,
|
|
168
|
+
* so it compiles, but its value cannot map to React Native and comes out `null`, which then fails
|
|
169
|
+
* the app's typecheck on the generated file. Nobody can use it as a class on a phone, so it is
|
|
170
|
+
* dropped here rather than banning a JavaScript word from every source file.
|
|
171
|
+
*/
|
|
172
|
+
function dropUnmappable(stylesheet) {
|
|
173
|
+
if (!Array.isArray(stylesheet.s))
|
|
174
|
+
return;
|
|
175
|
+
stylesheet.s = stylesheet.s.filter((entry) => !(Array.isArray(entry) && entry[0] === "transform"));
|
|
176
|
+
}
|
|
177
|
+
function colorOf(stylesheet, className, root) {
|
|
178
|
+
const entry = Array.isArray(stylesheet.s)
|
|
179
|
+
? stylesheet.s.find((each) => Array.isArray(each) && each[0] === className)
|
|
180
|
+
: undefined;
|
|
181
|
+
const hex = entry ? JSON.stringify(entry[1]).match(/#[0-9a-fA-F]{3,8}\b/)?.[0] : undefined;
|
|
182
|
+
if (hex === undefined)
|
|
183
|
+
throw new Error(`No colour for "${className}" in the compiled stylesheet. Tailwind writes only the classes ` +
|
|
184
|
+
`it finds in the app's files, so put "${className}" in a file under ${root} (a list of ` +
|
|
185
|
+
"the palette's classes beside the code that reads the colours keeps them found).");
|
|
186
|
+
return hex;
|
|
187
|
+
}
|
|
188
|
+
function missing(name) {
|
|
189
|
+
return (`${name} is not installed in the app. gusnips-native-css reads it from the app's own ` +
|
|
190
|
+
"packages: bun add react-native-css && bun add -d @tailwindcss/cli tailwindcss");
|
|
191
|
+
}
|
|
192
|
+
function isNotFound(error) {
|
|
193
|
+
return error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND";
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** What a resolver hands back to Metro. Only the `sourceFile` form is built here. */
|
|
2
|
+
export type Resolution = {
|
|
3
|
+
type: "sourceFile";
|
|
4
|
+
filePath: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: string;
|
|
7
|
+
};
|
|
8
|
+
/** Metro's `CustomResolver`, narrowed to the part this file reads. */
|
|
9
|
+
export type ResolveRequest = (context: ResolutionContext, moduleName: string, platform: string | null) => Resolution;
|
|
10
|
+
export interface ResolutionContext {
|
|
11
|
+
/** Metro's default resolver, for everything this one does not decide. */
|
|
12
|
+
readonly resolveRequest: ResolveRequest;
|
|
13
|
+
}
|
|
14
|
+
export interface MetroConfigInput {
|
|
15
|
+
readonly projectRoot?: string;
|
|
16
|
+
readonly watchFolders?: readonly string[];
|
|
17
|
+
readonly resolver?: {
|
|
18
|
+
readonly nodeModulesPaths?: readonly string[];
|
|
19
|
+
readonly resolveRequest?: ResolveRequest | null;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface NativeMetroOptions {
|
|
23
|
+
/**
|
|
24
|
+
* The monorepo's root folder, where the root `package.json` and `node_modules` are. Leave it out
|
|
25
|
+
* for an app that is not in a monorepo.
|
|
26
|
+
*/
|
|
27
|
+
monorepoRoot?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Wrap the config `@react-native/metro-config` gives you.
|
|
31
|
+
*
|
|
32
|
+
* ```js
|
|
33
|
+
* // metro.config.js
|
|
34
|
+
* const { getDefaultConfig } = require("@react-native/metro-config");
|
|
35
|
+
* const { nativeMetroConfig } = require("@gusnips/native/metro");
|
|
36
|
+
*
|
|
37
|
+
* module.exports = nativeMetroConfig(getDefaultConfig(__dirname), {
|
|
38
|
+
* monorepoRoot: require("node:path").resolve(__dirname, "../.."),
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* A resolver already on the config still runs for everything this one does not decide.
|
|
43
|
+
*/
|
|
44
|
+
export declare function nativeMetroConfig<C extends MetroConfigInput>(config: C, { monorepoRoot }?: NativeMetroOptions): C & {
|
|
45
|
+
watchFolders: string[];
|
|
46
|
+
resolver: {
|
|
47
|
+
nodeModulesPaths: string[];
|
|
48
|
+
resolveRequest: ResolveRequest;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=metro.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metro.d.ts","sourceRoot":"","sources":["../../src/node/metro.ts"],"names":[],"mappings":"AA4BA,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,KACpB,UAAU,CAAC;AAEhB,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC9C,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;KACjD,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,gBAAgB,EAC1D,MAAM,EAAE,CAAC,EACT,EAAE,YAAY,EAAE,GAAE,kBAAuB;;;;;;EAiC1C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Metro config a bare React Native app needs inside a bun monorepo.
|
|
3
|
+
*
|
|
4
|
+
* Three things, each learned by a crash:
|
|
5
|
+
*
|
|
6
|
+
* - **Metro does not find workspaces on its own.** `watchFolders` and `nodeModulesPaths` point it at
|
|
7
|
+
* the monorepo, so a workspace package that ships TypeScript source resolves like any other.
|
|
8
|
+
* (Symlinks and package `exports` are on by default since Metro 0.79 and 0.82.)
|
|
9
|
+
* - **One React, the app's.** bun's isolated store keeps a spare `react` at its root, and a package
|
|
10
|
+
* that forgets to declare react as a peer resolves `react` by walking up to THAT copy. Two Reacts
|
|
11
|
+
* in one bundle: the app's own screens render, and that package's hooks crash with "Cannot read
|
|
12
|
+
* property 'useMemo' of null". So every `react` and `react/*` import goes to the copy the app
|
|
13
|
+
* itself resolves.
|
|
14
|
+
* - **react-native-css's override module.** `react-native-css/native` re-exports a module named
|
|
15
|
+
* `react-native-css-metro-override` that throws on import unless Metro maps it to the package's
|
|
16
|
+
* own empty `override.js`. Without the mapping every styled component dies at launch with "Cannot
|
|
17
|
+
* read property 'useCssElement' of undefined". `withReactNativeCSS` does map it, and also swaps
|
|
18
|
+
* Metro's transformer for one that requires `@expo/metro-config`, which a bare app does not have.
|
|
19
|
+
* Only the mapping is taken here; the CSS itself is built ahead of time by `gusnips-native-css`.
|
|
20
|
+
* Skipped when the app does not use react-native-css.
|
|
21
|
+
*
|
|
22
|
+
* Metro's own types are not imported: this package does not depend on metro, and the few fields read
|
|
23
|
+
* here are stated below.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync } from "node:fs";
|
|
26
|
+
import { createRequire } from "node:module";
|
|
27
|
+
import { dirname, join } from "node:path";
|
|
28
|
+
/**
|
|
29
|
+
* Wrap the config `@react-native/metro-config` gives you.
|
|
30
|
+
*
|
|
31
|
+
* ```js
|
|
32
|
+
* // metro.config.js
|
|
33
|
+
* const { getDefaultConfig } = require("@react-native/metro-config");
|
|
34
|
+
* const { nativeMetroConfig } = require("@gusnips/native/metro");
|
|
35
|
+
*
|
|
36
|
+
* module.exports = nativeMetroConfig(getDefaultConfig(__dirname), {
|
|
37
|
+
* monorepoRoot: require("node:path").resolve(__dirname, "../.."),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* A resolver already on the config still runs for everything this one does not decide.
|
|
42
|
+
*/
|
|
43
|
+
export function nativeMetroConfig(config, { monorepoRoot } = {}) {
|
|
44
|
+
const { projectRoot } = config;
|
|
45
|
+
if (!projectRoot)
|
|
46
|
+
throw new Error("nativeMetroConfig: the config has no projectRoot. Pass it the config " +
|
|
47
|
+
"getDefaultConfig(__dirname) returns, which always sets one.");
|
|
48
|
+
const fromApp = createRequire(join(projectRoot, "package.json"));
|
|
49
|
+
const cssOverride = reactNativeCssOverride(fromApp);
|
|
50
|
+
const next = config.resolver?.resolveRequest ?? null;
|
|
51
|
+
const resolveRequest = (context, moduleName, platform) => {
|
|
52
|
+
if (cssOverride && moduleName.includes("react-native-css-metro-override"))
|
|
53
|
+
return { type: "sourceFile", filePath: cssOverride };
|
|
54
|
+
if (isReact(moduleName))
|
|
55
|
+
return { type: "sourceFile", filePath: fromApp.resolve(moduleName) };
|
|
56
|
+
return (next ?? context.resolveRequest)(context, moduleName, platform);
|
|
57
|
+
};
|
|
58
|
+
const extraRoots = monorepoRoot ? [monorepoRoot] : [];
|
|
59
|
+
return {
|
|
60
|
+
...config,
|
|
61
|
+
watchFolders: unique([...(config.watchFolders ?? []), ...extraRoots]),
|
|
62
|
+
resolver: {
|
|
63
|
+
...config.resolver,
|
|
64
|
+
nodeModulesPaths: unique([
|
|
65
|
+
...[projectRoot, ...extraRoots].map((root) => join(root, "node_modules")),
|
|
66
|
+
...(config.resolver?.nodeModulesPaths ?? []),
|
|
67
|
+
]),
|
|
68
|
+
resolveRequest,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `react` and its own entry points, and nothing that merely starts with the word: `react-native`,
|
|
74
|
+
* `react-dom` and `react-i18next` are packages of their own and resolve the normal way.
|
|
75
|
+
*
|
|
76
|
+
* ponytail: only react is forced to one copy, because only react has been seen twice. If another
|
|
77
|
+
* package turns up twice in a bundle (react-native itself is the likely next), add it here.
|
|
78
|
+
*/
|
|
79
|
+
function isReact(moduleName) {
|
|
80
|
+
return moduleName === "react" || moduleName.startsWith("react/");
|
|
81
|
+
}
|
|
82
|
+
/** The app's react-native-css `override.js`, or null when the app has no react-native-css. */
|
|
83
|
+
function reactNativeCssOverride(fromApp) {
|
|
84
|
+
let metroEntry;
|
|
85
|
+
try {
|
|
86
|
+
metroEntry = fromApp.resolve("react-native-css/metro");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const override = join(dirname(metroEntry), "override.js");
|
|
92
|
+
if (!existsSync(override))
|
|
93
|
+
throw new Error(`nativeMetroConfig: react-native-css is installed, but ${override} is not there. This ` +
|
|
94
|
+
"version keeps its override module somewhere else; @gusnips/native needs updating for it.");
|
|
95
|
+
return override;
|
|
96
|
+
}
|
|
97
|
+
function unique(paths) {
|
|
98
|
+
return [...new Set(paths)];
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=metro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metro.js","sourceRoot":"","sources":["../../src/node/metro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkC1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAS,EACT,EAAE,YAAY,EAAE,GAAuB,EAAE;IAEzC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,WAAW;QACd,MAAM,IAAI,KAAK,CACb,uEAAuE;YACrE,6DAA6D,CAChE,CAAC;IAEJ,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,cAAc,IAAI,IAAI,CAAC;IAErD,MAAM,cAAc,GAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE;QACvE,IAAI,WAAW,IAAI,UAAU,CAAC,QAAQ,CAAC,iCAAiC,CAAC;YACvE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QACvD,IAAI,OAAO,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9F,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IACzE,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,OAAO;QACL,GAAG,MAAM;QACT,YAAY,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC;QACrE,QAAQ,EAAE;YACR,GAAG,MAAM,CAAC,QAAQ;YAClB,gBAAgB,EAAE,MAAM,CAAC;gBACvB,GAAG,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;gBACzE,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,gBAAgB,IAAI,EAAE,CAAC;aAC7C,CAAC;YACF,cAAc;SACf;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,OAAO,CAAC,UAAkB;IACjC,OAAO,UAAU,KAAK,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACnE,CAAC;AAED,8FAA8F;AAC9F,SAAS,sBAAsB,CAAC,OAAoB;IAClD,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,yDAAyD,QAAQ,sBAAsB;YACrF,0FAA0F,CAC7F,CAAC;IACJ,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,KAAwB;IACtC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7B,CAAC"}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call once, before the first query runs, beside where you build the `QueryClient`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* installQueryManagers();
|
|
6
|
+
* export const queryClient = new QueryClient({ defaultOptions: queryDefaults() });
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* The managers are React Query's own singletons, so a second call replaces the first rather than
|
|
10
|
+
* adding a second listener.
|
|
11
|
+
*/
|
|
12
|
+
export declare function installQueryManagers(): void;
|
|
13
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAY3C"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Query on a phone: "focused" means the app is in the foreground, and "online" means NetInfo
|
|
3
|
+
* says so. React Query's own defaults read the browser's window and `navigator.onLine`, and a
|
|
4
|
+
* phone has neither.
|
|
5
|
+
*
|
|
6
|
+
* What this changes, so nobody expects more of it: a query's retries wait while the app is in the
|
|
7
|
+
* background or the phone is offline, and resume after; stale queries run again when the connection
|
|
8
|
+
* comes back (`refetchOnReconnect`, which `queryDefaults` turns on). It does NOT refetch when the app
|
|
9
|
+
* returns to the foreground, because `queryDefaults` leaves `refetchOnWindowFocus` off on purpose.
|
|
10
|
+
* The donor's comment said "refetch on app foreground" over a client where that option was off, so
|
|
11
|
+
* a screen still mounted from yesterday shows yesterday's data until something else refetches it.
|
|
12
|
+
* Pass `queryDefaults({ refetchOnWindowFocus: true })` if returning to the app should refresh.
|
|
13
|
+
*/
|
|
14
|
+
import NetInfo from "@react-native-community/netinfo";
|
|
15
|
+
import { focusManager, onlineManager } from "@tanstack/react-query";
|
|
16
|
+
import { AppState } from "react-native";
|
|
17
|
+
/**
|
|
18
|
+
* Call once, before the first query runs, beside where you build the `QueryClient`.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* installQueryManagers();
|
|
22
|
+
* export const queryClient = new QueryClient({ defaultOptions: queryDefaults() });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The managers are React Query's own singletons, so a second call replaces the first rather than
|
|
26
|
+
* adding a second listener.
|
|
27
|
+
*/
|
|
28
|
+
export function installQueryManagers() {
|
|
29
|
+
focusManager.setEventListener((setFocused) => {
|
|
30
|
+
const subscription = AppState.addEventListener("change", (state) => setFocused(state === "active"));
|
|
31
|
+
return () => subscription.remove();
|
|
32
|
+
});
|
|
33
|
+
// `!== false`, not `=== true`: NetInfo says `null` until it knows, and paused queries at every
|
|
34
|
+
// launch would be the price of reading that as offline.
|
|
35
|
+
onlineManager.setEventListener((setOnline) => NetInfo.addEventListener((state) => setOnline(state.isConnected !== false)));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,OAAO,MAAM,iCAAiC,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB;IAClC,YAAY,CAAC,gBAAgB,CAAC,CAAC,UAAU,EAAE,EAAE;QAC3C,MAAM,YAAY,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CACjE,UAAU,CAAC,KAAK,KAAK,QAAQ,CAAC,CAC/B,CAAC;QACF,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,+FAA+F;IAC/F,wDAAwD;IACxD,aAAa,CAAC,gBAAgB,CAAC,CAAC,SAAS,EAAE,EAAE,CAC3C,OAAO,CAAC,gBAAgB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAC5E,CAAC;AACJ,CAAC"}
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type SessionAdapter, type SseFrame } from "@gusnips/react";
|
|
2
|
+
export interface SseStreamOptions {
|
|
3
|
+
/** The whole address of the stream, `https://api.example.com/chat/stream`. */
|
|
4
|
+
url: string;
|
|
5
|
+
/** POST, the default, sends `body` as JSON. GET sends nothing. */
|
|
6
|
+
method?: "GET" | "POST";
|
|
7
|
+
body?: unknown;
|
|
8
|
+
/**
|
|
9
|
+
* The adapter your `createApiClient` uses. Its token goes out as `Authorization: Bearer`, and a
|
|
10
|
+
* 401 calls its `refresh()` once and opens again with the new token. Leave it out for a stream
|
|
11
|
+
* that needs no sign-in.
|
|
12
|
+
*/
|
|
13
|
+
session?: SessionAdapter;
|
|
14
|
+
/** Extra headers, computed when the request opens, like the API client's `headers`. */
|
|
15
|
+
headers?: () => Record<string, string>;
|
|
16
|
+
/** One frame off the wire. `data` is raw text: parse it the way your server writes it. */
|
|
17
|
+
onFrame: (frame: SseFrame) => void;
|
|
18
|
+
/**
|
|
19
|
+
* The stream failed. An `ApiError` when the server answered with a refusal, so the describer
|
|
20
|
+
* explains it like any other; a plain `Error` when nothing came back at all.
|
|
21
|
+
*/
|
|
22
|
+
onError?: (error: Error) => void;
|
|
23
|
+
/** The stream ended, cleanly or not, without `abort()` being called. */
|
|
24
|
+
onClose?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export interface SseStream {
|
|
27
|
+
readonly aborted: boolean;
|
|
28
|
+
/** Close the socket. No frame, error or close is delivered after this. */
|
|
29
|
+
abort(): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Open a stream. It starts at once; keep the handle to stop it.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* const stream = openSseStream({
|
|
36
|
+
* url: `${API_URL}/chat/stream`,
|
|
37
|
+
* body: { message },
|
|
38
|
+
* session, // the same adapter createApiClient uses
|
|
39
|
+
* onFrame: ({ event, data }) => handle(event, JSON.parse(data)),
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ponytail: XHR keeps the whole response in `responseText`, so memory grows with the length of the
|
|
44
|
+
* stream. A chat turn is fine. A feed left open all day is not; that needs a native streaming module.
|
|
45
|
+
*/
|
|
46
|
+
export declare function openSseStream(options: SseStreamOptions): SseStream;
|
|
47
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAyBA,OAAO,EAA6B,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/F,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,0FAA0F;IAC1F,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,0EAA0E;IAC1E,KAAK,IAAI,IAAI,CAAC;CACf;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAmFlE"}
|