@couch-kit/host 1.0.0 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@couch-kit/host",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "React Native host for local multiplayer party games on Android TV — WebSocket server, state management, and static file serving",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/assets.ts ADDED
@@ -0,0 +1,107 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Paths, Directory, File } from "expo-file-system";
3
+ import { Platform } from "react-native";
4
+ import { toErrorMessage } from "@couch-kit/core";
5
+
6
+ export interface AssetManifest {
7
+ files: string[];
8
+ }
9
+
10
+ export interface ExtractAssetsResult {
11
+ /** The filesystem path to the extracted www directory, or undefined if not ready */
12
+ staticDir: string | undefined;
13
+ /** Whether extraction is in progress */
14
+ loading: boolean;
15
+ /** Error message if extraction failed */
16
+ error: string | null;
17
+ }
18
+
19
+ /**
20
+ * Extracts bundled web assets from the APK to the filesystem so the native
21
+ * HTTP server can serve them.
22
+ *
23
+ * On Android, assets live inside the APK and cannot be served directly by
24
+ * a native HTTP server. This hook copies each file listed in the manifest
25
+ * from `asset:///www/<file>` to `<Paths.document>/www/<file>`.
26
+ *
27
+ * On iOS, assets are accessible from the bundle directory, so extraction
28
+ * is skipped and `staticDir` is returned as `undefined` (the server falls
29
+ * back to the bundle path).
30
+ *
31
+ * @param manifest - The asset manifest generated by `couch-kit bundle`.
32
+ * Contains a `files` array listing all relative paths.
33
+ */
34
+ export function useExtractAssets(manifest: AssetManifest): ExtractAssetsResult {
35
+ const [staticDir, setStaticDir] = useState<string | undefined>(undefined);
36
+ const [loading, setLoading] = useState(true);
37
+ const [error, setError] = useState<string | null>(null);
38
+
39
+ useEffect(() => {
40
+ // iOS: skip extraction, server uses bundle path fallback
41
+ if (Platform.OS !== "android") {
42
+ setLoading(false);
43
+ return;
44
+ }
45
+
46
+ let cancelled = false;
47
+
48
+ const extractAssets = async () => {
49
+ try {
50
+ const documentDir = Paths.document;
51
+ const targetDir = new Directory(documentDir, "www");
52
+
53
+ // Clean previous extraction to ensure fresh assets after app updates
54
+ if (targetDir.exists) {
55
+ targetDir.delete();
56
+ }
57
+
58
+ // Create the root target directory
59
+ targetDir.create({ intermediates: true });
60
+
61
+ // Copy each file from APK assets to filesystem
62
+ for (const filePath of manifest.files) {
63
+ if (cancelled) return;
64
+
65
+ const sourceFile = new File(`asset:///www/${filePath}`);
66
+ const destFile = new File(targetDir, filePath);
67
+
68
+ // Ensure subdirectory exists (e.g., "assets/" in "assets/index.js")
69
+ const lastSlash = filePath.lastIndexOf("/");
70
+ if (lastSlash > 0) {
71
+ const subDir = new Directory(
72
+ targetDir,
73
+ filePath.substring(0, lastSlash),
74
+ );
75
+ subDir.create({ intermediates: true, idempotent: true });
76
+ }
77
+
78
+ sourceFile.copy(destFile);
79
+ }
80
+
81
+ if (cancelled) return;
82
+
83
+ // Strip file:// prefix for the native HTTP server
84
+ const rawPath = targetDir.uri.replace(/^file:\/\//, "");
85
+ setStaticDir(rawPath);
86
+ } catch (e) {
87
+ if (!cancelled) {
88
+ const message = toErrorMessage(e);
89
+ console.warn("[CouchKit] Asset extraction failed:", message);
90
+ setError(message);
91
+ }
92
+ } finally {
93
+ if (!cancelled) {
94
+ setLoading(false);
95
+ }
96
+ }
97
+ };
98
+
99
+ extractAssets();
100
+
101
+ return () => {
102
+ cancelled = true;
103
+ };
104
+ }, [manifest]);
105
+
106
+ return { staticDir, loading, error };
107
+ }
@@ -2,6 +2,9 @@
2
2
  * Minimal type definition for the raw TCP socket provided by react-native-tcp-socket.
3
3
  * Covers only the API surface used by GameWebSocketServer.
4
4
  */
5
+
6
+ import type { Buffer } from "buffer";
7
+
5
8
  export interface TcpSocketInstance {
6
9
  write(data: string | Buffer): void;
7
10
  destroy(): void;
package/src/index.tsx CHANGED
@@ -1,4 +1,5 @@
1
- export * from './provider';
2
- export * from './server';
3
- export * from './websocket';
4
- export * from './network';
1
+ export * from "./provider";
2
+ export * from "./server";
3
+ export * from "./websocket";
4
+ export * from "./network";
5
+ export * from "./assets";
package/src/server.ts CHANGED
@@ -51,9 +51,9 @@ export const useStaticServer = (config: CouchKitHostConfig) => {
51
51
  try {
52
52
  // Use staticDir if provided (required on Android where bundle path is undefined),
53
53
  // otherwise fall back to the iOS bundle directory via expo-file-system
54
+ const bundleUri = Paths.bundle.uri;
54
55
  const path =
55
- config.staticDir ||
56
- `${Paths.bundle.uri.replace(/^file:\/\//, "")}www`;
56
+ config.staticDir || `${bundleUri.replace(/^file:\/\//, "")}www`;
57
57
  const port = config.port || DEFAULT_HTTP_PORT;
58
58
 
59
59
  server = new StaticServer();