@gusnips/native 0.0.0-stage → 0.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +310 -2
  3. package/dist/device-locale.d.ts +15 -0
  4. package/dist/device-locale.d.ts.map +1 -0
  5. package/dist/device-locale.js +27 -0
  6. package/dist/device-locale.js.map +1 -0
  7. package/dist/i18n.d.ts +34 -0
  8. package/dist/i18n.d.ts.map +1 -0
  9. package/dist/i18n.js +63 -0
  10. package/dist/i18n.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +18 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/network.d.ts +18 -0
  16. package/dist/network.d.ts.map +1 -0
  17. package/dist/network.js +35 -0
  18. package/dist/network.js.map +1 -0
  19. package/dist/node/bin/gusnips-native-css.d.ts +3 -0
  20. package/dist/node/bin/gusnips-native-css.d.ts.map +1 -0
  21. package/dist/node/bin/gusnips-native-css.js +75 -0
  22. package/dist/node/bin/gusnips-native-css.js.map +1 -0
  23. package/dist/node/css.d.ts +44 -0
  24. package/dist/node/css.d.ts.map +1 -0
  25. package/dist/node/css.js +181 -0
  26. package/dist/node/css.js.map +1 -0
  27. package/dist/node/metro.d.ts +51 -0
  28. package/dist/node/metro.d.ts.map +1 -0
  29. package/dist/node/metro.js +100 -0
  30. package/dist/node/metro.js.map +1 -0
  31. package/dist/query.d.ts +13 -0
  32. package/dist/query.d.ts.map +1 -0
  33. package/dist/query.js +37 -0
  34. package/dist/query.js.map +1 -0
  35. package/dist/sse.d.ts +47 -0
  36. package/dist/sse.d.ts.map +1 -0
  37. package/dist/sse.js +135 -0
  38. package/dist/sse.js.map +1 -0
  39. package/dist/supabase.d.ts +18 -0
  40. package/dist/supabase.d.ts.map +1 -0
  41. package/dist/supabase.js +29 -0
  42. package/dist/supabase.js.map +1 -0
  43. package/dist/theme.d.ts +25 -0
  44. package/dist/theme.d.ts.map +1 -0
  45. package/dist/theme.js +99 -0
  46. package/dist/theme.js.map +1 -0
  47. package/package.json +150 -4
  48. package/src/device-locale.test.ts +33 -0
  49. package/src/device-locale.ts +28 -0
  50. package/src/i18n.ts +91 -0
  51. package/src/index.ts +18 -0
  52. package/src/network.ts +37 -0
  53. package/src/node/bin/gusnips-native-css.ts +84 -0
  54. package/src/node/css.ts +246 -0
  55. package/src/node/metro.test.ts +66 -0
  56. package/src/node/metro.ts +143 -0
  57. package/src/query.ts +41 -0
  58. package/src/sse.test.ts +195 -0
  59. package/src/sse.ts +174 -0
  60. package/src/supabase.ts +29 -0
  61. package/src/theme.test.ts +92 -0
  62. 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,44 @@
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
+ //# 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"}
@@ -0,0 +1,181 @@
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
+ * lightningcss 1.30.2 breaks react-native-css's compiler. react-native-css checks for it and means
119
+ * to throw, but the check sits inside a `try` whose `catch` is empty, so the throw is swallowed and
120
+ * the build goes on. This asks the same copy its loader picks: the one under `@expo/metro-config`
121
+ * first (a peer of react-native-css, so bun installs it), then its own.
122
+ *
123
+ * That order is also why a devDependency on lightningcss does not fix it. Pin it with `overrides`
124
+ * in the root `package.json`, which reaches every copy.
125
+ */
126
+ function refuseBrokenLightningcss(fromApp) {
127
+ const fromCss = createRequire(fromApp.resolve("react-native-css/package.json"));
128
+ const candidates = [];
129
+ try {
130
+ candidates.push(createRequire(fromCss.resolve("@expo/metro-config/package.json")));
131
+ }
132
+ catch {
133
+ // No expo config beside it: react-native-css loads its own lightningcss.
134
+ }
135
+ candidates.push(fromCss);
136
+ for (const from of candidates) {
137
+ let version;
138
+ try {
139
+ const manifest = from("lightningcss/package.json");
140
+ version = manifest.version;
141
+ }
142
+ catch {
143
+ continue;
144
+ }
145
+ if (version === "1.30.2")
146
+ throw new Error("lightningcss 1.30.2 breaks react-native-css's compiler. Pin it to 1.30.1 with " +
147
+ '"overrides": { "lightningcss": "1.30.1" } in the root package.json, or move to a newer one.');
148
+ return;
149
+ }
150
+ }
151
+ /**
152
+ * Tailwind finds class names by reading source as text, so a React Native style key such as
153
+ * `transform` (an Animated style needs one) becomes a candidate. `transform` IS a Tailwind utility,
154
+ * so it compiles, but its value cannot map to React Native and comes out `null`, which then fails
155
+ * the app's typecheck on the generated file. Nobody can use it as a class on a phone, so it is
156
+ * dropped here rather than banning a JavaScript word from every source file.
157
+ */
158
+ function dropUnmappable(stylesheet) {
159
+ if (!Array.isArray(stylesheet.s))
160
+ return;
161
+ stylesheet.s = stylesheet.s.filter((entry) => !(Array.isArray(entry) && entry[0] === "transform"));
162
+ }
163
+ function colorOf(stylesheet, className, root) {
164
+ const entry = Array.isArray(stylesheet.s)
165
+ ? stylesheet.s.find((each) => Array.isArray(each) && each[0] === className)
166
+ : undefined;
167
+ const hex = entry ? JSON.stringify(entry[1]).match(/#[0-9a-fA-F]{3,8}\b/)?.[0] : undefined;
168
+ if (hex === undefined)
169
+ throw new Error(`No colour for "${className}" in the compiled stylesheet. Tailwind writes only the classes ` +
170
+ `it finds in the app's files, so put "${className}" in a file under ${root} (a list of ` +
171
+ "the palette's classes beside the code that reads the colours keeps them found).");
172
+ return hex;
173
+ }
174
+ function missing(name) {
175
+ return (`${name} is not installed in the app. gusnips-native-css reads it from the app's own ` +
176
+ "packages: bun add react-native-css && bun add -d @tailwindcss/cli tailwindcss");
177
+ }
178
+ function isNotFound(error) {
179
+ return error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND";
180
+ }
181
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6C7D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAO,GAAqB,EAAE;IACjE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,GAAG,GAAG,CAAC,CAAC;IAE9F,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IAC1D,gGAAgG;IAChG,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;IAC1E,cAAc,CAAC,UAAU,CAAC,CAAC;IAE3B,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,2CAA2C,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,gCAAgC,CAAC;IAChH,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,MAAM,SAAS,CACb,cAAc,EACd,GAAG,MAAM,kFAAkF;QACzF,uDAAuD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CACzF,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,cAAc,CAAC,CAAC;IAE/B,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5B,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,CAAC,IAAwB,EAAE,EAAE,CAC1C,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;YAC7C,IAAI;YACJ,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;SACvC,CAAC,CACH,CAAC;QACJ,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAChE,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC7C,MAAM,SAAS,CACb,UAAU,EACV,GAAG,MAAM,2BAA2B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CACzE,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAoB,EAAE,IAAY,EAAE,KAAa;IAC1E,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,2FAA2F;IAC3F,gFAAgF;IAChF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE;QACrE,GAAG,EAAE,IAAI;QACT,QAAQ,EAAE,OAAO;QACjB,wFAAwF;QACxF,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACjG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,iCAAiC,KAAK,EAAE;YACtC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,0BAA0B,IAAI,GAAG,CAC1F,CAAC;IACJ,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,OAAoB;IACvC,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,QAAQ,GAA8C,OAAO,CAAC,YAAY,CAAC,CAAC;IAClF,MAAM,GAAG,GAAG,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;IAC5F,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,YAAY,4BAA4B,CAAC,CAAC;IAC3F,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,OAAoB;IACxC,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,CAAC;IACd,CAAC;IACD,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,wBAAwB,CAAC,OAAoB;IACpD,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,CAAC;IAChF,MAAM,UAAU,GAAkB,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAwB,IAAI,CAAC,2BAA2B,CAAC,CAAC;YACxE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,QAAQ;YACtB,MAAM,IAAI,KAAK,CACb,gFAAgF;gBAC9E,6FAA6F,CAChG,CAAC;QACJ,OAAO;IACT,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,UAAoC;IAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO;IACzC,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAChC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,UAAoC,EAAE,SAAiB,EAAE,IAAY;IACpF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;QAC3E,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,IAAI,GAAG,KAAK,SAAS;QACnB,MAAM,IAAI,KAAK,CACb,kBAAkB,SAAS,iEAAiE;YAC1F,wCAAwC,SAAS,qBAAqB,IAAI,cAAc;YACxF,iFAAiF,CACpF,CAAC;IACJ,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,CACL,GAAG,IAAI,+EAA+E;QACtF,+EAA+E,CAChF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,CAAC;AACxF,CAAC"}
@@ -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":"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"}
@@ -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"}