react-native-gtkx 0.4.0-alpha.2 → 0.4.0-alpha.3

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.
@@ -49,7 +49,7 @@ type ResolveResult = ResolvedEntity | AmbiguousResult | NotFoundResult;
49
49
  */
50
50
  declare const resolveComponent: (query: string) => ResolveResult;
51
51
  /** Formats a resolved entity into the plain object the tool returns as
52
- * JSON text — one shape per kind, matching what docs/api.md and
53
- * docs/platform-layer.md actually document for it. */
52
+ * JSON text — one shape per kind, matching what docs/reference/ and
53
+ * docs/architecture/ actually document for it. */
54
54
  declare const formatEntity: (entity: Entity) => Record<string, unknown>;
55
55
  export { formatEntity, resolveComponent, type Candidate, type Entity, type ResolveResult, };
@@ -103,8 +103,8 @@ const resolveComponent = (query) => {
103
103
  return { status: "not-found", query: trimmed, suggestions };
104
104
  };
105
105
  /** Formats a resolved entity into the plain object the tool returns as
106
- * JSON text — one shape per kind, matching what docs/api.md and
107
- * docs/platform-layer.md actually document for it. */
106
+ * JSON text — one shape per kind, matching what docs/reference/ and
107
+ * docs/architecture/ actually document for it. */
108
108
  const formatEntity = (entity) => {
109
109
  switch (entity.kind) {
110
110
  case "portable-component":
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/mcp/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["// Name resolution for rn_gtkx_describe_component: turns a free-form name\n// into exactly one of the surface's entities, or says clearly why it\n// could not (ambiguous vs. not-found) instead of guessing.\nimport {\n ADW_WIDGETS,\n COMMON_PRIMITIVES,\n GTK_WIDGETS,\n PORTABLE_APIS,\n PORTABLE_COMPONENTS,\n type CommonRecord,\n type PortableRecord,\n type WidgetRecord,\n} from \"./data/generated.js\"\n\ntype Entity =\n | { kind: \"portable-component\"; record: PortableRecord }\n | { kind: \"portable-api\"; record: PortableRecord }\n | { kind: \"common\"; record: CommonRecord }\n | { kind: \"gtk-widget\"; record: WidgetRecord }\n | { kind: \"adw-widget\"; record: WidgetRecord }\n\ntype Candidate = { name: string; subpath: string; kind: Entity[\"kind\"] }\n\ntype ResolvedEntity = {\n status: \"resolved\"\n entity: Entity\n matchedBy: \"exact\" | \"case-insensitive\" | \"prefix\" | \"substring\"\n}\ntype AmbiguousResult = {\n status: \"ambiguous\"\n query: string\n candidates: readonly Candidate[]\n}\ntype NotFoundResult = {\n status: \"not-found\"\n query: string\n suggestions: readonly Candidate[]\n}\ntype ResolveResult = ResolvedEntity | AmbiguousResult | NotFoundResult\n\nconst ALL_ENTITIES: readonly Entity[] = [\n ...PORTABLE_COMPONENTS.map((record): Entity => ({\n kind: \"portable-component\",\n record,\n })),\n ...PORTABLE_APIS.map((record): Entity => ({ kind: \"portable-api\", record })),\n ...COMMON_PRIMITIVES.map((record): Entity => ({ kind: \"common\", record })),\n ...GTK_WIDGETS.map((record): Entity => ({ kind: \"gtk-widget\", record })),\n ...ADW_WIDGETS.map((record): Entity => ({ kind: \"adw-widget\", record })),\n]\n\nconst candidateOf = (entity: Entity): Candidate => ({\n name: entity.record.name,\n subpath: entity.record.subpath,\n kind: entity.kind,\n})\n\n// Unweighted Levenshtein distance — 200-odd short names, no need for\n// anything fancier to rank \"closest known name\" suggestions.\nconst levenshtein = (a: string, b: string): number => {\n const rows = a.length + 1\n const cols = b.length + 1\n const dp: number[][] = Array.from({ length: rows }, () =>\n new Array<number>(cols).fill(0),\n )\n for (let i = 0; i < rows; i++) {\n dp[i]![0] = i\n }\n for (let j = 0; j < cols; j++) {\n dp[0]![j] = j\n }\n for (let i = 1; i < rows; i++) {\n for (let j = 1; j < cols; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i]![j] = Math.min(\n dp[i - 1]![j]! + 1,\n dp[i]![j - 1]! + 1,\n dp[i - 1]![j - 1]! + cost,\n )\n }\n }\n return dp[rows - 1]![cols - 1]!\n}\n\nconst MAX_CANDIDATES = 20\nconst MAX_SUGGESTIONS = 5\nconst PREFIXES = [\"Gtk\", \"Adw\"] as const\n\n/**\n * Resolves a free-form name to exactly one surface entity:\n * 1. exact match, then case-insensitive exact match;\n * 2. Gtk<name>/Adw<name> exact match — tried BEFORE substring search, so\n * \"Popover\" resolves cleanly to GtkPopover instead of landing ambiguous\n * against GtkPopoverBin/GtkPopoverMenu/GtkPopoverMenuBar too;\n * 3. substring search (case-insensitive): one hit resolves, several are\n * reported as ambiguous candidates (never guessed), zero fall through\n * to a not-found response with the closest known names by edit\n * distance.\n */\nconst resolveComponent = (query: string): ResolveResult => {\n const trimmed = query.trim()\n const lower = trimmed.toLowerCase()\n\n if (lower.length === 0) {\n return { status: \"not-found\", query: trimmed, suggestions: [] }\n }\n\n const exact = ALL_ENTITIES.find((e) => e.record.name === trimmed)\n if (exact) {\n return { status: \"resolved\", entity: exact, matchedBy: \"exact\" }\n }\n\n const caseInsensitive = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === lower,\n )\n if (caseInsensitive) {\n return {\n status: \"resolved\",\n entity: caseInsensitive,\n matchedBy: \"case-insensitive\",\n }\n }\n\n if (!/^(gtk|adw)/i.test(trimmed)) {\n for (const prefix of PREFIXES) {\n const prefixedLower = `${prefix}${trimmed}`.toLowerCase()\n const match = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === prefixedLower,\n )\n if (match) {\n return { status: \"resolved\", entity: match, matchedBy: \"prefix\" }\n }\n }\n }\n\n const substringMatches = ALL_ENTITIES.filter((e) =>\n e.record.name.toLowerCase().includes(lower),\n )\n if (substringMatches.length === 1) {\n return {\n status: \"resolved\",\n entity: substringMatches[0]!,\n matchedBy: \"substring\",\n }\n }\n if (substringMatches.length > 1) {\n return {\n status: \"ambiguous\",\n query: trimmed,\n candidates: substringMatches.slice(0, MAX_CANDIDATES).map(candidateOf),\n }\n }\n\n const suggestions = ALL_ENTITIES.map((entity) => ({\n entity,\n distance: levenshtein(lower, entity.record.name.toLowerCase()),\n }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, MAX_SUGGESTIONS)\n .map(({ entity }) => candidateOf(entity))\n\n return { status: \"not-found\", query: trimmed, suggestions }\n}\n\n/** Formats a resolved entity into the plain object the tool returns as\n * JSON text — one shape per kind, matching what docs/api.md and\n * docs/platform-layer.md actually document for it. */\nconst formatEntity = (entity: Entity): Record<string, unknown> => {\n switch (entity.kind) {\n case \"portable-component\":\n case \"portable-api\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n ...(record.gtkImplementation !== undefined\n ? { gtkImplementation: record.gtkImplementation }\n : {}),\n supported: record.supported,\n differences: record.differences,\n }\n }\n case \"common\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n summary: record.summary,\n }\n }\n case \"gtk-widget\":\n case \"adw-widget\": {\n const record = entity.record\n const note = record.wrapped\n ? \"Takes `style` + `onLayout`; React Native layout positions and styles it. Outside RN layout (e.g. inside an AdwHeaderBar slot) it steps aside and renders as the bare widget.\"\n : `Raw export — exactly as gtkx binds it, no \\`style\\`/\\`onLayout\\` (wrap it yourself with wrapReactNative from react-native-gtkx/gtk if you need RN layout to drive it).${record.reason ? ` Why raw: ${record.reason}` : \"\"}`\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n wrapped: record.wrapped,\n ...(record.reason !== undefined ? { reason: record.reason } : {}),\n note,\n }\n }\n }\n}\n\nexport {\n formatEntity,\n resolveComponent,\n type Candidate,\n type Entity,\n type ResolveResult,\n}\n"]}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/mcp/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["// Name resolution for rn_gtkx_describe_component: turns a free-form name\n// into exactly one of the surface's entities, or says clearly why it\n// could not (ambiguous vs. not-found) instead of guessing.\nimport {\n ADW_WIDGETS,\n COMMON_PRIMITIVES,\n GTK_WIDGETS,\n PORTABLE_APIS,\n PORTABLE_COMPONENTS,\n type CommonRecord,\n type PortableRecord,\n type WidgetRecord,\n} from \"./data/generated.js\"\n\ntype Entity =\n | { kind: \"portable-component\"; record: PortableRecord }\n | { kind: \"portable-api\"; record: PortableRecord }\n | { kind: \"common\"; record: CommonRecord }\n | { kind: \"gtk-widget\"; record: WidgetRecord }\n | { kind: \"adw-widget\"; record: WidgetRecord }\n\ntype Candidate = { name: string; subpath: string; kind: Entity[\"kind\"] }\n\ntype ResolvedEntity = {\n status: \"resolved\"\n entity: Entity\n matchedBy: \"exact\" | \"case-insensitive\" | \"prefix\" | \"substring\"\n}\ntype AmbiguousResult = {\n status: \"ambiguous\"\n query: string\n candidates: readonly Candidate[]\n}\ntype NotFoundResult = {\n status: \"not-found\"\n query: string\n suggestions: readonly Candidate[]\n}\ntype ResolveResult = ResolvedEntity | AmbiguousResult | NotFoundResult\n\nconst ALL_ENTITIES: readonly Entity[] = [\n ...PORTABLE_COMPONENTS.map((record): Entity => ({\n kind: \"portable-component\",\n record,\n })),\n ...PORTABLE_APIS.map((record): Entity => ({ kind: \"portable-api\", record })),\n ...COMMON_PRIMITIVES.map((record): Entity => ({ kind: \"common\", record })),\n ...GTK_WIDGETS.map((record): Entity => ({ kind: \"gtk-widget\", record })),\n ...ADW_WIDGETS.map((record): Entity => ({ kind: \"adw-widget\", record })),\n]\n\nconst candidateOf = (entity: Entity): Candidate => ({\n name: entity.record.name,\n subpath: entity.record.subpath,\n kind: entity.kind,\n})\n\n// Unweighted Levenshtein distance — 200-odd short names, no need for\n// anything fancier to rank \"closest known name\" suggestions.\nconst levenshtein = (a: string, b: string): number => {\n const rows = a.length + 1\n const cols = b.length + 1\n const dp: number[][] = Array.from({ length: rows }, () =>\n new Array<number>(cols).fill(0),\n )\n for (let i = 0; i < rows; i++) {\n dp[i]![0] = i\n }\n for (let j = 0; j < cols; j++) {\n dp[0]![j] = j\n }\n for (let i = 1; i < rows; i++) {\n for (let j = 1; j < cols; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i]![j] = Math.min(\n dp[i - 1]![j]! + 1,\n dp[i]![j - 1]! + 1,\n dp[i - 1]![j - 1]! + cost,\n )\n }\n }\n return dp[rows - 1]![cols - 1]!\n}\n\nconst MAX_CANDIDATES = 20\nconst MAX_SUGGESTIONS = 5\nconst PREFIXES = [\"Gtk\", \"Adw\"] as const\n\n/**\n * Resolves a free-form name to exactly one surface entity:\n * 1. exact match, then case-insensitive exact match;\n * 2. Gtk<name>/Adw<name> exact match — tried BEFORE substring search, so\n * \"Popover\" resolves cleanly to GtkPopover instead of landing ambiguous\n * against GtkPopoverBin/GtkPopoverMenu/GtkPopoverMenuBar too;\n * 3. substring search (case-insensitive): one hit resolves, several are\n * reported as ambiguous candidates (never guessed), zero fall through\n * to a not-found response with the closest known names by edit\n * distance.\n */\nconst resolveComponent = (query: string): ResolveResult => {\n const trimmed = query.trim()\n const lower = trimmed.toLowerCase()\n\n if (lower.length === 0) {\n return { status: \"not-found\", query: trimmed, suggestions: [] }\n }\n\n const exact = ALL_ENTITIES.find((e) => e.record.name === trimmed)\n if (exact) {\n return { status: \"resolved\", entity: exact, matchedBy: \"exact\" }\n }\n\n const caseInsensitive = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === lower,\n )\n if (caseInsensitive) {\n return {\n status: \"resolved\",\n entity: caseInsensitive,\n matchedBy: \"case-insensitive\",\n }\n }\n\n if (!/^(gtk|adw)/i.test(trimmed)) {\n for (const prefix of PREFIXES) {\n const prefixedLower = `${prefix}${trimmed}`.toLowerCase()\n const match = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === prefixedLower,\n )\n if (match) {\n return { status: \"resolved\", entity: match, matchedBy: \"prefix\" }\n }\n }\n }\n\n const substringMatches = ALL_ENTITIES.filter((e) =>\n e.record.name.toLowerCase().includes(lower),\n )\n if (substringMatches.length === 1) {\n return {\n status: \"resolved\",\n entity: substringMatches[0]!,\n matchedBy: \"substring\",\n }\n }\n if (substringMatches.length > 1) {\n return {\n status: \"ambiguous\",\n query: trimmed,\n candidates: substringMatches.slice(0, MAX_CANDIDATES).map(candidateOf),\n }\n }\n\n const suggestions = ALL_ENTITIES.map((entity) => ({\n entity,\n distance: levenshtein(lower, entity.record.name.toLowerCase()),\n }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, MAX_SUGGESTIONS)\n .map(({ entity }) => candidateOf(entity))\n\n return { status: \"not-found\", query: trimmed, suggestions }\n}\n\n/** Formats a resolved entity into the plain object the tool returns as\n * JSON text — one shape per kind, matching what docs/reference/ and\n * docs/architecture/ actually document for it. */\nconst formatEntity = (entity: Entity): Record<string, unknown> => {\n switch (entity.kind) {\n case \"portable-component\":\n case \"portable-api\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n ...(record.gtkImplementation !== undefined\n ? { gtkImplementation: record.gtkImplementation }\n : {}),\n supported: record.supported,\n differences: record.differences,\n }\n }\n case \"common\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n summary: record.summary,\n }\n }\n case \"gtk-widget\":\n case \"adw-widget\": {\n const record = entity.record\n const note = record.wrapped\n ? \"Takes `style` + `onLayout`; React Native layout positions and styles it. Outside RN layout (e.g. inside an AdwHeaderBar slot) it steps aside and renders as the bare widget.\"\n : `Raw export — exactly as gtkx binds it, no \\`style\\`/\\`onLayout\\` (wrap it yourself with wrapReactNative from react-native-gtkx/gtk if you need RN layout to drive it).${record.reason ? ` Why raw: ${record.reason}` : \"\"}`\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n wrapped: record.wrapped,\n ...(record.reason !== undefined ? { reason: record.reason } : {}),\n note,\n }\n }\n }\n}\n\nexport {\n formatEntity,\n resolveComponent,\n type Candidate,\n type Entity,\n type ResolveResult,\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  // generated data in ./data/generated.ts — no @gtkx/* import anywhere in
3
3
  // this module or its dependents, so this runs on any OS without GTK
4
4
  // installed (an agent reading a react-native-gtkx project on a Mac, for
5
- // instance). See docs/getting-started.md for how a consumer registers it.
5
+ // instance). See docs/guide/toolchains.md for how a consumer registers it.
6
6
  import { createRequire } from "node:module";
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -97,7 +97,7 @@ const describeComponentTool = () => defineTool({
97
97
  const searchDocsTool = () => defineTool({
98
98
  name: "rn_gtkx_search_docs",
99
99
  title: "Search react-native-gtkx docs",
100
- description: "Free-text search over the docs (getting-started, the platform layer, " +
100
+ description: "Free-text search over the docs (the guide, the platform layer, " +
101
101
  "gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it " +
102
102
  "for symptoms and known-issue questions that rn_gtkx_describe_component " +
103
103
  "cannot answer by name (workarounds are keyed by mechanism, not by " +
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["// The MCP server itself: three tools over stdio, backed entirely by the\n// generated data in ./data/generated.ts — no @gtkx/* import anywhere in\n// this module or its dependents, so this runs on any OS without GTK\n// installed (an agent reading a react-native-gtkx project on a Mac, for\n// instance). See docs/getting-started.md for how a consumer registers it.\nimport { createRequire } from \"node:module\"\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { listSurface, SURFACE_AREAS } from \"./list.js\"\nimport { formatEntity, resolveComponent } from \"./resolve.js\"\nimport { searchDocs } from \"./search.js\"\nimport {\n defineTool,\n registerTool,\n textContent,\n textError,\n type Tool,\n} from \"./tool.js\"\n\nconst require = createRequire(import.meta.url)\n// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist\n// -> package root). package.json ships in every npm tarball regardless of\n// the \"files\" allowlist, so this needs no extra packaging step.\nconst { version } = require(\"../../package.json\") as { version: string }\n\nconst SERVER_NAME = \"react-native-gtkx-mcp\"\nconst DEFAULT_SEARCH_LIMIT = 10\nconst MAX_SEARCH_LIMIT = 20\n\nconst listSurfaceShape = {\n area: z\n .enum(SURFACE_AREAS)\n .optional()\n .describe(\n \"Area to list. Omit for an overview with counts across all areas: \" +\n SURFACE_AREAS.join(\", \") +\n \".\",\n ),\n}\n\nconst describeComponentShape = {\n name: z\n .string()\n .describe(\n \"A component/API/widget name: a portable react-native export (e.g. \" +\n '\"FlatList\"), a react-native-gtkx/gtk or /adw widget with or ' +\n 'without its Gtk/Adw prefix (e.g. \"Popover\" resolves to ' +\n '\"GtkPopover\"), or a react-native-gtkx/common export (e.g. ' +\n '\"NavigationStack\"). Case-insensitive.',\n ),\n}\n\nconst searchDocsShape = {\n query: z\n .string()\n .describe(\n \"Free-text query — a symptom, a GTK term, an option name. Use this \" +\n \"when rn_gtkx_describe_component does not have a name to look up, \" +\n 'e.g. \"known issues\", \"chrome content\", \"windowSize\".',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(\n `Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`,\n ),\n}\n\nconst listSurfaceTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_list_surface\",\n title: \"List the react-native-gtkx surface\",\n description:\n \"Browse the react-native-gtkx surface without knowing an exact name first: \" +\n \"portable react-native components/APIs, the react-native-gtkx/gtk and \" +\n \"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. \" +\n \"Without `area`, returns counts and where to look next.\",\n inputSchema: listSurfaceShape,\n handler: ({ area }) => textContent(listSurface(area)),\n })\n\nconst describeComponentTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_describe_component\",\n title: \"Describe a react-native-gtkx component\",\n description:\n \"Look up one component/widget/API by name: does it exist, which \" +\n \"subpath it is exported from, what GTK widget backs it (for \" +\n \"portable components), what differs from React Native, and whether \" +\n \"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The \" +\n \"single most useful tool here — start with this before \" +\n \"rn_gtkx_search_docs.\",\n inputSchema: describeComponentShape,\n handler: ({ name }) => {\n const result = resolveComponent(name)\n if (result.status === \"resolved\") {\n const body = {\n matchedBy: result.matchedBy,\n ...formatEntity(result.entity),\n }\n return textContent(JSON.stringify(body, null, 2))\n }\n if (result.status === \"ambiguous\") {\n const lines = result.candidates\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `\"${result.query}\" matches ${result.candidates.length} names — call again with one of:\\n${lines}`,\n )\n }\n const lines = result.suggestions\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `No component named \"${result.query}\".` +\n (lines.length > 0\n ? ` Closest known names:\\n${lines}`\n : \" rn_gtkx_list_surface has the full index.\"),\n )\n },\n })\n\nconst searchDocsTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_search_docs\",\n title: \"Search react-native-gtkx docs\",\n description:\n \"Free-text search over the docs (getting-started, the platform layer, \" +\n \"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it \" +\n \"for symptoms and known-issue questions that rn_gtkx_describe_component \" +\n \"cannot answer by name (workarounds are keyed by mechanism, not by \" +\n \"component).\",\n inputSchema: searchDocsShape,\n handler: ({ query, limit }) => {\n const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT)\n if (results.length === 0) {\n return textContent(\n `No matches for \"${query}\". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`,\n )\n }\n const body = results\n .map((r) => `## ${r.doc} — ${r.heading}\\n\\n${r.text}`)\n .join(\"\\n\\n---\\n\\n\")\n return textContent(body)\n },\n })\n\nconst buildTools = (): Tool[] => [\n listSurfaceTool(),\n describeComponentTool(),\n searchDocsTool(),\n]\n\nconst createMcpServer = (): McpServer => {\n const server = new McpServer({ name: SERVER_NAME, version })\n for (const tool of buildTools()) {\n registerTool(server, tool)\n }\n return server\n}\n\nconst main = async (): Promise<void> => {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nexport { buildTools, createMcpServer, main, SERVER_NAME }\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["// The MCP server itself: three tools over stdio, backed entirely by the\n// generated data in ./data/generated.ts — no @gtkx/* import anywhere in\n// this module or its dependents, so this runs on any OS without GTK\n// installed (an agent reading a react-native-gtkx project on a Mac, for\n// instance). See docs/guide/toolchains.md for how a consumer registers it.\nimport { createRequire } from \"node:module\"\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { listSurface, SURFACE_AREAS } from \"./list.js\"\nimport { formatEntity, resolveComponent } from \"./resolve.js\"\nimport { searchDocs } from \"./search.js\"\nimport {\n defineTool,\n registerTool,\n textContent,\n textError,\n type Tool,\n} from \"./tool.js\"\n\nconst require = createRequire(import.meta.url)\n// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist\n// -> package root). package.json ships in every npm tarball regardless of\n// the \"files\" allowlist, so this needs no extra packaging step.\nconst { version } = require(\"../../package.json\") as { version: string }\n\nconst SERVER_NAME = \"react-native-gtkx-mcp\"\nconst DEFAULT_SEARCH_LIMIT = 10\nconst MAX_SEARCH_LIMIT = 20\n\nconst listSurfaceShape = {\n area: z\n .enum(SURFACE_AREAS)\n .optional()\n .describe(\n \"Area to list. Omit for an overview with counts across all areas: \" +\n SURFACE_AREAS.join(\", \") +\n \".\",\n ),\n}\n\nconst describeComponentShape = {\n name: z\n .string()\n .describe(\n \"A component/API/widget name: a portable react-native export (e.g. \" +\n '\"FlatList\"), a react-native-gtkx/gtk or /adw widget with or ' +\n 'without its Gtk/Adw prefix (e.g. \"Popover\" resolves to ' +\n '\"GtkPopover\"), or a react-native-gtkx/common export (e.g. ' +\n '\"NavigationStack\"). Case-insensitive.',\n ),\n}\n\nconst searchDocsShape = {\n query: z\n .string()\n .describe(\n \"Free-text query — a symptom, a GTK term, an option name. Use this \" +\n \"when rn_gtkx_describe_component does not have a name to look up, \" +\n 'e.g. \"known issues\", \"chrome content\", \"windowSize\".',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(\n `Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`,\n ),\n}\n\nconst listSurfaceTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_list_surface\",\n title: \"List the react-native-gtkx surface\",\n description:\n \"Browse the react-native-gtkx surface without knowing an exact name first: \" +\n \"portable react-native components/APIs, the react-native-gtkx/gtk and \" +\n \"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. \" +\n \"Without `area`, returns counts and where to look next.\",\n inputSchema: listSurfaceShape,\n handler: ({ area }) => textContent(listSurface(area)),\n })\n\nconst describeComponentTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_describe_component\",\n title: \"Describe a react-native-gtkx component\",\n description:\n \"Look up one component/widget/API by name: does it exist, which \" +\n \"subpath it is exported from, what GTK widget backs it (for \" +\n \"portable components), what differs from React Native, and whether \" +\n \"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The \" +\n \"single most useful tool here — start with this before \" +\n \"rn_gtkx_search_docs.\",\n inputSchema: describeComponentShape,\n handler: ({ name }) => {\n const result = resolveComponent(name)\n if (result.status === \"resolved\") {\n const body = {\n matchedBy: result.matchedBy,\n ...formatEntity(result.entity),\n }\n return textContent(JSON.stringify(body, null, 2))\n }\n if (result.status === \"ambiguous\") {\n const lines = result.candidates\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `\"${result.query}\" matches ${result.candidates.length} names — call again with one of:\\n${lines}`,\n )\n }\n const lines = result.suggestions\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `No component named \"${result.query}\".` +\n (lines.length > 0\n ? ` Closest known names:\\n${lines}`\n : \" rn_gtkx_list_surface has the full index.\"),\n )\n },\n })\n\nconst searchDocsTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_search_docs\",\n title: \"Search react-native-gtkx docs\",\n description:\n \"Free-text search over the docs (the guide, the platform layer, \" +\n \"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it \" +\n \"for symptoms and known-issue questions that rn_gtkx_describe_component \" +\n \"cannot answer by name (workarounds are keyed by mechanism, not by \" +\n \"component).\",\n inputSchema: searchDocsShape,\n handler: ({ query, limit }) => {\n const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT)\n if (results.length === 0) {\n return textContent(\n `No matches for \"${query}\". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`,\n )\n }\n const body = results\n .map((r) => `## ${r.doc} — ${r.heading}\\n\\n${r.text}`)\n .join(\"\\n\\n---\\n\\n\")\n return textContent(body)\n },\n })\n\nconst buildTools = (): Tool[] => [\n listSurfaceTool(),\n describeComponentTool(),\n searchDocsTool(),\n]\n\nconst createMcpServer = (): McpServer => {\n const server = new McpServer({ name: SERVER_NAME, version })\n for (const tool of buildTools()) {\n registerTool(server, tool)\n }\n return server\n}\n\nconst main = async (): Promise<void> => {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nexport { buildTools, createMcpServer, main, SERVER_NAME }\n"]}
@@ -38,7 +38,7 @@
38
38
  // resolved once here, at bundle time (see ./gtkx-config-module.ts).
39
39
  //
40
40
  // Not attempted here: the vite path. Investigated, not a drop-in of this
41
- // same technique — see docs/getting-started.md's "Shipping an app"
41
+ // same technique — see docs/guide/packaging.md's "Choosing an artifact"
42
42
  // section for the concrete blocker found empirically while building this:
43
43
  // the vite bundle loads the native addon through a DYNAMICALLY obtained
44
44
  // require (`createRequire(import.meta.url)("./gtkx.node")`), which a
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/sea/bundle.ts"],"names":[],"mappings":"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","sourcesContent":["// Bundles a Metro-built react-native-gtkx app (main.jsbundle) into a\n// single CJS file suitable for Node's --experimental-sea-config, undoing\n// exactly the externalization HOST_MODULE_EXTERNALS performs at Metro\n// build time.\n//\n// Design, and where this follows/diverges from gtkx's own tutorial\n// (gtkx-org/gtkx examples/tutorial/scripts/bundle.ts and\n// bundle-postject.ts — read before any of this was written):\n//\n// - Bundling mechanics (bundle → single minified CJS file) and the\n// SEA/postject pipeline itself (./assemble.ts) follow the tutorial\n// closely. The bundler is rolldown rather than the tutorial's esbuild —\n// NOT a preference: rolldown is vite's own engine (vite 8 depends on it\n// outright, esbuild is only an optional peer there), and vite is a hard\n// dependency of @gtkx/cli, which is a hard dependency of this package.\n// So rolldown is already installed for every consumer, and esbuild would\n// have been the one genuinely new bundler in the tree.\n// - Native addon: the tutorial's shim assumes the addon file sits BESIDE\n// the built executable (require resolved from\n// dirname(process.execPath)). That is two files, not one — the actual\n// central design decision this epic exists to make — so this diverges:\n// the addon is embedded IN the artifact (as a SEA asset, or as a base64\n// literal for the --standalone .cjs) and extracted to a per-user cache\n// directory on first run. See ./native-shim.ts's header for the full\n// reasoning.\n// - Metro's externals: the tutorial has no equivalent problem, its source\n// is already a single vite/rollup bundle. Metro deliberately\n// externalizes @gtkx/*, react and yoga-layout (../metro/index.ts,\n// HOST_MODULE_EXTERNALS) so main.jsbundle expects\n// globalThis.__hostModules/__hostRequire to exist before it runs (see\n// ../runner/host.ts). This module is a third host implementation\n// (host.ts and host-dev.ts are the other two — see host-dev.ts's header\n// for why they don't share code) that builds those globals from real,\n// STATICALLY imported modules — every name in HOST_MODULE_EXTERNALS\n// becomes a literal `import * as` statement in the generated entry — so\n// the bundler can inline them instead of the app needing a runtime\n// node_modules to dynamically load them from. gtkx.config.ts is also\n// resolved once here, at bundle time (see ./gtkx-config-module.ts).\n//\n// Not attempted here: the vite path. Investigated, not a drop-in of this\n// same technique — see docs/getting-started.md's \"Shipping an app\"\n// section for the concrete blocker found empirically while building this:\n// the vite bundle loads the native addon through a DYNAMICALLY obtained\n// require (`createRequire(import.meta.url)(\"./gtkx.node\")`), which a\n// bundler does not intercept the way it intercepts a static import\n// (verified: the resolve hook never fires for it), and which is unlikely\n// to resolve at all once running from inside a real SEA blob, where\n// import.meta.url no longer points at a file with real siblings on disk.\nimport { mkdirSync, readdirSync, readFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join } from \"node:path\"\nimport { rolldown } from \"rolldown\"\nimport { HOST_MODULE_EXTERNALS } from \"../metro/index.js\"\nimport {\n buildGtkxConfigModule,\n virtualConfigModulePlugin,\n} from \"./gtkx-config-module.js\"\nimport {\n NATIVE_ASSET_KEY,\n nativeAddonShimPlugin,\n type NativeAddonSource,\n} from \"./native-shim.js\"\nimport { reactAnchorPlugin } from \"./react-anchor-plugin.js\"\n\nexport type MetroSeaBundleOptions = {\n /** The app root — where gtkx.config.ts lives and package.json resolves\n * HOST_MODULE_EXTERNALS' bare specifiers (react, @gtkx/*, ...) from. */\n appRoot: string\n /** Path to the release jsbundle produced by `build-linux`. */\n jsbundlePath: string\n /** Where to write the bundled CJS entry (e.g. dist/bundle.cjs). */\n outFile: string\n /** Where the native addon's bytes come from at runtime — \"sea-asset\"\n * for a real single executable, \"inline\" for a self-contained .cjs run\n * by a system Node (default: \"sea-asset\"). */\n nativeAddonSource?: NativeAddonSource\n}\n\nexport type NativeAddonAsset = {\n /** Absolute path to the platform's @gtkx/native-<platform>-<libc>.node\n * that npm installed for this machine. */\n path: string\n /** The SEA asset key it must be embedded under. */\n key: string\n}\n\n/**\n * Locates the ONE native addon file npm installed for this machine.\n * @gtkx/native picks exactly one optionalDependency per platform/libc at\n * install time (see @gtkx/native/index.js's NAPI-RS-generated loader) —\n * failing loudly on anything other than exactly one match matters here:\n * silently picking the wrong one would embed an addon for the wrong\n * architecture with no error until the app actually launches.\n */\nexport const resolveNativeAddon = (appRoot: string): NativeAddonAsset => {\n const appRequire = createRequire(join(appRoot, \"package.json\"))\n const nativePackageJson = appRequire.resolve(\"@gtkx/native/package.json\")\n // .../node_modules/@gtkx/native/package.json -> .../node_modules/@gtkx\n const scopeDir = dirname(dirname(nativePackageJson))\n const candidates = readdirSync(scopeDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"native-\"))\n .flatMap((entry) => {\n const dir = join(scopeDir, entry.name)\n return readdirSync(dir)\n .filter((file) => file.endsWith(\".node\"))\n .map((file) => join(dir, file))\n })\n const [first, ...rest] = candidates\n if (!first || rest.length > 0) {\n throw new Error(\n `expected exactly one @gtkx/native-*/*.node under ${scopeDir}, found ` +\n `${candidates.length} (${candidates.join(\", \") || \"none\"}) — ` +\n \"reinstall targeting a single platform.\",\n )\n }\n return { path: first, key: NATIVE_ASSET_KEY }\n}\n\n/**\n * Builds the synthetic SEA entry source: every HOST_MODULE_EXTERNALS name\n * loaded through a dynamic `import()` (mirroring runner/host.ts's own\n * `load()`, which does the same against a real node_modules at runtime),\n * assembled into globalThis.__hostModules, then the jsbundle text executed\n * the same way host.ts does.\n *\n * Dynamic import, not static, throughout: a Node SEA's main script is\n * CommonJS only — confirmed empirically, Node 24 executes the embedded\n * main as CJS regardless of a \"type\": \"module\" field in sea-config.json\n * or an .mjs extension — and top-level await is a hard error under a \"cjs\"\n * output format REGARDLESS of whether the module is reached via a static\n * or dynamic import (the restriction is format-wide, not per-module —\n * verified: switching yoga-layout's static import to a dynamic one alone\n * did not clear the error). yoga-layout's own entry point has exactly this\n * top-level await (it loads its WASM binary asynchronously — see\n * node_modules/yoga-layout/dist/src/index.js: `const Yoga =\n * wrapAssembly(await loadYoga())` at module scope). The actual fix: import\n * \"yoga-layout/load\" instead, which exports the same `await`, just inside\n * an async FUNCTION (`export async function loadYoga()`), not at the\n * file's top level — bundleable, and awaited here like everything else.\n */\nexport const buildEntrySource = (jsbundlePath: string): string => {\n const jsbundleSource = readFileSync(jsbundlePath, \"utf8\")\n const loadLines = HOST_MODULE_EXTERNALS.map((name) => {\n if (name === \"yoga-layout\") {\n return [\n ` {`,\n ` const { loadYoga, ...rest } = await import(\"yoga-layout/load\");`,\n ` hostModules[\"yoga-layout\"] = __interopHostModule({ ...rest, default: await loadYoga() });`,\n ` }`,\n ].join(\"\\n\")\n }\n return ` hostModules[${JSON.stringify(name)}] = __interopHostModule(await import(${JSON.stringify(name)}));`\n })\n return [\n `async function __gtkxSeaMain() {`,\n // Mirrors runner/host.ts's `load()` interop: import() handles both ESM\n // and CJS, merge a `default` for Babel's default-import interop when\n // the module has none of its own.\n ` const __interopHostModule = (ns) => {`,\n ` const merged = Object.assign({ __esModule: true }, ns);`,\n ` if (!(\"default\" in ns)) merged.default = ns;`,\n ` return merged;`,\n ` };`,\n ` const hostModules = {};`,\n ...loadLines,\n ` globalThis.__hostModules = hostModules;`,\n // Node builtin proxies resolve lazily through the host's own require —\n // real here, no createRequire indirection needed (see host.ts for why\n // it needs one: this file's import.meta.url is never used for\n // anything relative, only require() of bare/builtin specifiers).\n ` globalThis.__hostRequire = require;`,\n ` require(\"node:vm\").runInThisContext(${JSON.stringify(jsbundleSource)}, { filename: ${JSON.stringify(jsbundlePath)} });`,\n `}`,\n `__gtkxSeaMain().catch((error) => { console.error(error); process.exitCode = 1; });`,\n ].join(\"\\n\")\n}\n\n/**\n * Produces `outFile`, a single CJS file with the jsbundle, its\n * HOST_MODULE_EXTERNALS and gtkx.config.ts's resolved values all inlined,\n * and the native addon import redirected to the SEA-asset extraction\n * shim. Returns the native addon that must be embedded as a SEA asset\n * under {@link NativeAddonAsset.key} for the result to run.\n */\nexport const bundleMetroSea = async (\n options: MetroSeaBundleOptions,\n): Promise<NativeAddonAsset> => {\n const { appRoot, jsbundlePath, outFile } = options\n const nativeAddonSource = options.nativeAddonSource ?? \"sea-asset\"\n const configModuleSource = await buildGtkxConfigModule(appRoot)\n const nativeAddon = resolveNativeAddon(appRoot)\n const entrySource = buildEntrySource(jsbundlePath)\n\n mkdirSync(dirname(outFile), { recursive: true })\n\n // The entry is given a path inside appRoot that does not exist on disk,\n // rather than a `\\0`-prefixed virtual id: every bare specifier it\n // imports (react, @gtkx/*, yoga-layout) must resolve from the app's own\n // node_modules, and a real directory is what makes that happen.\n const entryId = join(appRoot, \"__gtkx-sea-entry.js\")\n\n const bundle = await rolldown({\n input: entryId,\n cwd: appRoot,\n platform: \"node\",\n plugins: [\n {\n name: \"gtkx-sea-entry\",\n resolveId: (source) => (source === entryId ? entryId : null),\n load: (id) => (id === entryId ? entrySource : null),\n },\n reactAnchorPlugin(appRoot),\n nativeAddonShimPlugin({\n specifier: \"@gtkx/native\",\n appRoot,\n source: nativeAddonSource,\n assetKey: nativeAddon.key,\n addonBytes:\n nativeAddonSource === \"inline\"\n ? readFileSync(nativeAddon.path)\n : undefined,\n }),\n virtualConfigModulePlugin(configModuleSource, appRoot),\n ],\n })\n try {\n await bundle.write({\n file: outFile,\n format: \"cjs\",\n minify: true,\n // Every HOST_MODULE_EXTERNALS name is reached through a dynamic\n // import; without this each one becomes its own chunk and the\n // \"single file\" the whole build exists to produce is a directory.\n codeSplitting: false,\n })\n } finally {\n await bundle.close()\n }\n\n return nativeAddon\n}\n"]}
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/sea/bundle.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,yEAAyE;AACzE,sEAAsE;AACtE,cAAc;AACd,EAAE;AACF,mEAAmE;AACnE,yDAAyD;AACzD,6DAA6D;AAC7D,EAAE;AACF,mEAAmE;AACnE,qEAAqE;AACrE,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,2EAA2E;AAC3E,yDAAyD;AACzD,yEAAyE;AACzE,gDAAgD;AAChD,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,yEAAyE;AACzE,uEAAuE;AACvE,eAAe;AACf,0EAA0E;AAC1E,+DAA+D;AAC/D,oEAAoE;AACpE,oDAAoD;AACpD,wEAAwE;AACxE,mEAAmE;AACnE,0EAA0E;AAC1E,wEAAwE;AACxE,sEAAsE;AACtE,0EAA0E;AAC1E,qEAAqE;AACrE,uEAAuE;AACvE,sEAAsE;AACtE,EAAE;AACF,yEAAyE;AACzE,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AACxE,qEAAqE;AACrE,mEAAmE;AACnE,yEAAyE;AACzE,oEAAoE;AACpE,yEAAyE;AACzE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EACL,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAwB5D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAAoB,EAAE;IACtE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAA;IAC/D,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAA;IACzE,uEAAuE;IACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAA;IACpD,MAAM,UAAU,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC9D,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;SAC1E,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACtC,OAAO,WAAW,CAAC,GAAG,CAAC;aACpB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IACJ,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,UAAU,CAAA;IACnC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,oDAAoD,QAAQ,UAAU;YACpE,GAAG,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,MAAM;YAC9D,wCAAwC,CAC3C,CAAA;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAA;AAC/C,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,YAAoB,EAAU,EAAE;IAC/D,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;IACzD,MAAM,SAAS,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACnD,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YAC3B,OAAO;gBACL,KAAK;gBACL,qEAAqE;gBACrE,+FAA+F;gBAC/F,KAAK;aACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACd,CAAC;QACD,OAAO,iBAAiB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,wCAAwC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAA;IAC/G,CAAC,CAAC,CAAA;IACF,OAAO;QACL,kCAAkC;QAClC,uEAAuE;QACvE,qEAAqE;QACrE,kCAAkC;QAClC,yCAAyC;QACzC,6DAA6D;QAC7D,kDAAkD;QAClD,oBAAoB;QACpB,MAAM;QACN,2BAA2B;QAC3B,GAAG,SAAS;QACZ,2CAA2C;QAC3C,uEAAuE;QACvE,sEAAsE;QACtE,8DAA8D;QAC9D,iEAAiE;QACjE,uCAAuC;QACvC,yCAAyC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM;QAC1H,GAAG;QACH,oFAAoF;KACrF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAA8B,EACH,EAAE;IAC7B,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAClD,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,WAAW,CAAA;IAClE,MAAM,kBAAkB,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC/D,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;IAC/C,MAAM,WAAW,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAElD,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAEhD,wEAAwE;IACxE,kEAAkE;IAClE,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAA;IAEpD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;QAC5B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,OAAO;QACZ,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,gBAAgB;gBACtB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC5D,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;aACpD;YACD,iBAAiB,CAAC,OAAO,CAAC;YAC1B,qBAAqB,CAAC;gBACpB,SAAS,EAAE,cAAc;gBACzB,OAAO;gBACP,MAAM,EAAE,iBAAiB;gBACzB,QAAQ,EAAE,WAAW,CAAC,GAAG;gBACzB,UAAU,EACR,iBAAiB,KAAK,QAAQ;oBAC5B,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC;oBAChC,CAAC,CAAC,SAAS;aAChB,CAAC;YACF,yBAAyB,CAAC,kBAAkB,EAAE,OAAO,CAAC;SACvD;KACF,CAAC,CAAA;IACF,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC;YACjB,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,IAAI;YACZ,gEAAgE;YAChE,8DAA8D;YAC9D,kEAAkE;YAClE,aAAa,EAAE,KAAK;SACrB,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA","sourcesContent":["// Bundles a Metro-built react-native-gtkx app (main.jsbundle) into a\n// single CJS file suitable for Node's --experimental-sea-config, undoing\n// exactly the externalization HOST_MODULE_EXTERNALS performs at Metro\n// build time.\n//\n// Design, and where this follows/diverges from gtkx's own tutorial\n// (gtkx-org/gtkx examples/tutorial/scripts/bundle.ts and\n// bundle-postject.ts — read before any of this was written):\n//\n// - Bundling mechanics (bundle → single minified CJS file) and the\n// SEA/postject pipeline itself (./assemble.ts) follow the tutorial\n// closely. The bundler is rolldown rather than the tutorial's esbuild —\n// NOT a preference: rolldown is vite's own engine (vite 8 depends on it\n// outright, esbuild is only an optional peer there), and vite is a hard\n// dependency of @gtkx/cli, which is a hard dependency of this package.\n// So rolldown is already installed for every consumer, and esbuild would\n// have been the one genuinely new bundler in the tree.\n// - Native addon: the tutorial's shim assumes the addon file sits BESIDE\n// the built executable (require resolved from\n// dirname(process.execPath)). That is two files, not one — the actual\n// central design decision this epic exists to make — so this diverges:\n// the addon is embedded IN the artifact (as a SEA asset, or as a base64\n// literal for the --standalone .cjs) and extracted to a per-user cache\n// directory on first run. See ./native-shim.ts's header for the full\n// reasoning.\n// - Metro's externals: the tutorial has no equivalent problem, its source\n// is already a single vite/rollup bundle. Metro deliberately\n// externalizes @gtkx/*, react and yoga-layout (../metro/index.ts,\n// HOST_MODULE_EXTERNALS) so main.jsbundle expects\n// globalThis.__hostModules/__hostRequire to exist before it runs (see\n// ../runner/host.ts). This module is a third host implementation\n// (host.ts and host-dev.ts are the other two — see host-dev.ts's header\n// for why they don't share code) that builds those globals from real,\n// STATICALLY imported modules — every name in HOST_MODULE_EXTERNALS\n// becomes a literal `import * as` statement in the generated entry — so\n// the bundler can inline them instead of the app needing a runtime\n// node_modules to dynamically load them from. gtkx.config.ts is also\n// resolved once here, at bundle time (see ./gtkx-config-module.ts).\n//\n// Not attempted here: the vite path. Investigated, not a drop-in of this\n// same technique — see docs/guide/packaging.md's \"Choosing an artifact\"\n// section for the concrete blocker found empirically while building this:\n// the vite bundle loads the native addon through a DYNAMICALLY obtained\n// require (`createRequire(import.meta.url)(\"./gtkx.node\")`), which a\n// bundler does not intercept the way it intercepts a static import\n// (verified: the resolve hook never fires for it), and which is unlikely\n// to resolve at all once running from inside a real SEA blob, where\n// import.meta.url no longer points at a file with real siblings on disk.\nimport { mkdirSync, readdirSync, readFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join } from \"node:path\"\nimport { rolldown } from \"rolldown\"\nimport { HOST_MODULE_EXTERNALS } from \"../metro/index.js\"\nimport {\n buildGtkxConfigModule,\n virtualConfigModulePlugin,\n} from \"./gtkx-config-module.js\"\nimport {\n NATIVE_ASSET_KEY,\n nativeAddonShimPlugin,\n type NativeAddonSource,\n} from \"./native-shim.js\"\nimport { reactAnchorPlugin } from \"./react-anchor-plugin.js\"\n\nexport type MetroSeaBundleOptions = {\n /** The app root — where gtkx.config.ts lives and package.json resolves\n * HOST_MODULE_EXTERNALS' bare specifiers (react, @gtkx/*, ...) from. */\n appRoot: string\n /** Path to the release jsbundle produced by `build-linux`. */\n jsbundlePath: string\n /** Where to write the bundled CJS entry (e.g. dist/bundle.cjs). */\n outFile: string\n /** Where the native addon's bytes come from at runtime — \"sea-asset\"\n * for a real single executable, \"inline\" for a self-contained .cjs run\n * by a system Node (default: \"sea-asset\"). */\n nativeAddonSource?: NativeAddonSource\n}\n\nexport type NativeAddonAsset = {\n /** Absolute path to the platform's @gtkx/native-<platform>-<libc>.node\n * that npm installed for this machine. */\n path: string\n /** The SEA asset key it must be embedded under. */\n key: string\n}\n\n/**\n * Locates the ONE native addon file npm installed for this machine.\n * @gtkx/native picks exactly one optionalDependency per platform/libc at\n * install time (see @gtkx/native/index.js's NAPI-RS-generated loader) —\n * failing loudly on anything other than exactly one match matters here:\n * silently picking the wrong one would embed an addon for the wrong\n * architecture with no error until the app actually launches.\n */\nexport const resolveNativeAddon = (appRoot: string): NativeAddonAsset => {\n const appRequire = createRequire(join(appRoot, \"package.json\"))\n const nativePackageJson = appRequire.resolve(\"@gtkx/native/package.json\")\n // .../node_modules/@gtkx/native/package.json -> .../node_modules/@gtkx\n const scopeDir = dirname(dirname(nativePackageJson))\n const candidates = readdirSync(scopeDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"native-\"))\n .flatMap((entry) => {\n const dir = join(scopeDir, entry.name)\n return readdirSync(dir)\n .filter((file) => file.endsWith(\".node\"))\n .map((file) => join(dir, file))\n })\n const [first, ...rest] = candidates\n if (!first || rest.length > 0) {\n throw new Error(\n `expected exactly one @gtkx/native-*/*.node under ${scopeDir}, found ` +\n `${candidates.length} (${candidates.join(\", \") || \"none\"}) — ` +\n \"reinstall targeting a single platform.\",\n )\n }\n return { path: first, key: NATIVE_ASSET_KEY }\n}\n\n/**\n * Builds the synthetic SEA entry source: every HOST_MODULE_EXTERNALS name\n * loaded through a dynamic `import()` (mirroring runner/host.ts's own\n * `load()`, which does the same against a real node_modules at runtime),\n * assembled into globalThis.__hostModules, then the jsbundle text executed\n * the same way host.ts does.\n *\n * Dynamic import, not static, throughout: a Node SEA's main script is\n * CommonJS only — confirmed empirically, Node 24 executes the embedded\n * main as CJS regardless of a \"type\": \"module\" field in sea-config.json\n * or an .mjs extension — and top-level await is a hard error under a \"cjs\"\n * output format REGARDLESS of whether the module is reached via a static\n * or dynamic import (the restriction is format-wide, not per-module —\n * verified: switching yoga-layout's static import to a dynamic one alone\n * did not clear the error). yoga-layout's own entry point has exactly this\n * top-level await (it loads its WASM binary asynchronously — see\n * node_modules/yoga-layout/dist/src/index.js: `const Yoga =\n * wrapAssembly(await loadYoga())` at module scope). The actual fix: import\n * \"yoga-layout/load\" instead, which exports the same `await`, just inside\n * an async FUNCTION (`export async function loadYoga()`), not at the\n * file's top level — bundleable, and awaited here like everything else.\n */\nexport const buildEntrySource = (jsbundlePath: string): string => {\n const jsbundleSource = readFileSync(jsbundlePath, \"utf8\")\n const loadLines = HOST_MODULE_EXTERNALS.map((name) => {\n if (name === \"yoga-layout\") {\n return [\n ` {`,\n ` const { loadYoga, ...rest } = await import(\"yoga-layout/load\");`,\n ` hostModules[\"yoga-layout\"] = __interopHostModule({ ...rest, default: await loadYoga() });`,\n ` }`,\n ].join(\"\\n\")\n }\n return ` hostModules[${JSON.stringify(name)}] = __interopHostModule(await import(${JSON.stringify(name)}));`\n })\n return [\n `async function __gtkxSeaMain() {`,\n // Mirrors runner/host.ts's `load()` interop: import() handles both ESM\n // and CJS, merge a `default` for Babel's default-import interop when\n // the module has none of its own.\n ` const __interopHostModule = (ns) => {`,\n ` const merged = Object.assign({ __esModule: true }, ns);`,\n ` if (!(\"default\" in ns)) merged.default = ns;`,\n ` return merged;`,\n ` };`,\n ` const hostModules = {};`,\n ...loadLines,\n ` globalThis.__hostModules = hostModules;`,\n // Node builtin proxies resolve lazily through the host's own require —\n // real here, no createRequire indirection needed (see host.ts for why\n // it needs one: this file's import.meta.url is never used for\n // anything relative, only require() of bare/builtin specifiers).\n ` globalThis.__hostRequire = require;`,\n ` require(\"node:vm\").runInThisContext(${JSON.stringify(jsbundleSource)}, { filename: ${JSON.stringify(jsbundlePath)} });`,\n `}`,\n `__gtkxSeaMain().catch((error) => { console.error(error); process.exitCode = 1; });`,\n ].join(\"\\n\")\n}\n\n/**\n * Produces `outFile`, a single CJS file with the jsbundle, its\n * HOST_MODULE_EXTERNALS and gtkx.config.ts's resolved values all inlined,\n * and the native addon import redirected to the SEA-asset extraction\n * shim. Returns the native addon that must be embedded as a SEA asset\n * under {@link NativeAddonAsset.key} for the result to run.\n */\nexport const bundleMetroSea = async (\n options: MetroSeaBundleOptions,\n): Promise<NativeAddonAsset> => {\n const { appRoot, jsbundlePath, outFile } = options\n const nativeAddonSource = options.nativeAddonSource ?? \"sea-asset\"\n const configModuleSource = await buildGtkxConfigModule(appRoot)\n const nativeAddon = resolveNativeAddon(appRoot)\n const entrySource = buildEntrySource(jsbundlePath)\n\n mkdirSync(dirname(outFile), { recursive: true })\n\n // The entry is given a path inside appRoot that does not exist on disk,\n // rather than a `\\0`-prefixed virtual id: every bare specifier it\n // imports (react, @gtkx/*, yoga-layout) must resolve from the app's own\n // node_modules, and a real directory is what makes that happen.\n const entryId = join(appRoot, \"__gtkx-sea-entry.js\")\n\n const bundle = await rolldown({\n input: entryId,\n cwd: appRoot,\n platform: \"node\",\n plugins: [\n {\n name: \"gtkx-sea-entry\",\n resolveId: (source) => (source === entryId ? entryId : null),\n load: (id) => (id === entryId ? entrySource : null),\n },\n reactAnchorPlugin(appRoot),\n nativeAddonShimPlugin({\n specifier: \"@gtkx/native\",\n appRoot,\n source: nativeAddonSource,\n assetKey: nativeAddon.key,\n addonBytes:\n nativeAddonSource === \"inline\"\n ? readFileSync(nativeAddon.path)\n : undefined,\n }),\n virtualConfigModulePlugin(configModuleSource, appRoot),\n ],\n })\n try {\n await bundle.write({\n file: outFile,\n format: \"cjs\",\n minify: true,\n // Every HOST_MODULE_EXTERNALS name is reached through a dynamic\n // import; without this each one becomes its own chunk and the\n // \"single file\" the whole build exists to produce is a directory.\n codeSplitting: false,\n })\n } finally {\n await bundle.close()\n }\n\n return nativeAddon\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  // The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon
2
- // loaded through dlopen — see docs/getting-started.md's "Shipping an app"
3
- // section for the full design reasoning) cannot be embedded as bundled JS:
2
+ // loaded through dlopen — see docs/guide/packaging.md for the shipped
3
+ // behavior, and this file's own comments below for the full design
4
+ // reasoning) cannot be embedded as bundled JS:
4
5
  // a Node SEA is a V8 code cache blob, and dlopen needs a real file on a
5
6
  // real filesystem, not bytes inside the executable.
6
7
  //
@@ -1 +1 @@
1
- {"version":3,"file":"native-shim.js","sourceRoot":"","sources":["../../src/sea/native-shim.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,qBAAqB;AACrB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAgBlD,MAAM,eAAe,GAAG,CACtB,MAAyB,EACzB,QAAgB,EACR,EAAE,CACV,MAAM,KAAK,WAAW;IACpB,CAAC,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;IAC1E,CAAC,CAAC,2CAA2C,CAAA;AAEjD,MAAM,UAAU,GAAG,CACjB,MAAyB,EACzB,QAAgB,EAChB,MAAc,EACN,EAAE,CAAC;;;;;EAKX,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;gBAEpE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDhD,CAAA;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAA+B,EACvB,EAAE;IACV,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAC9C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,UAAU,CACzB,MAAM,EACN,OAAO,CAAC,QAAQ,IAAI,gBAAgB,EACpC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAA;IACD,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;KAChD,CAAA;AACH,CAAC,CAAA","sourcesContent":["// The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon\n// loaded through dlopen — see docs/getting-started.md's \"Shipping an app\"\n// section for the full design reasoning) cannot be embedded as bundled JS:\n// a Node SEA is a V8 code cache blob, and dlopen needs a real file on a\n// real filesystem, not bytes inside the executable.\n//\n// So it has to be carried as bytes and written back to disk before use.\n// This plugin swaps every import of a given specifier (in practice,\n// \"@gtkx/native\" — see ../sea/bundle.ts) for a shim that does exactly\n// that: extract to a per-user cache directory (content-hash-keyed, so\n// repeat launches reuse the file instead of re-extracting ~1.6 MB every\n// start) and dlopen it from there. Where the bytes come from is the one\n// difference between the two single-file artifacts — Node's SEA \"assets\"\n// mechanism for the executable, a base64 literal for the standalone .cjs;\n// see {@link NativeAddonSource}.\n//\n// Cache location: XDG_CACHE_HOME (or ~/.cache) first, os.tmpdir() as a\n// fallback for a read-only $HOME. Deliberately NOT the SEA executable's\n// own directory — that may be read-only (a mounted image, a root-owned\n// install under /opt) or simply a different place than where the addon\n// was extracted the first time, if the binary moved. This is the same\n// question gtkx's own tutorial answers by placing the addon BESIDE the\n// executable (dist/gtkx.node next to dist/app) — a real divergence, not an\n// oversight: that keeps two files, which is exactly what this epic set\n// out to stop doing.\nimport { join } from \"node:path\"\nimport type { Plugin } from \"rolldown\"\n\nexport const NATIVE_ASSET_KEY = \"gtkx-native.node\"\n\n/**\n * Where the shim gets the addon's bytes from — the only difference between\n * the two single-file artifacts:\n *\n * - `\"sea-asset\"`: from the SEA blob, via node:sea. Only works inside a\n * real single executable.\n * - `\"inline\"`: from a base64 literal in the bundle itself, so the CJS\n * file is self-contained under a plain `node app.cjs` — the same\n * artifact minus the embedded Node runtime. Costs ~4/3 of the addon's\n * size in source text (the addon is ~1.6 MB) and is parsed once at\n * startup; the extraction and dlopen below are shared.\n */\nexport type NativeAddonSource = \"sea-asset\" | \"inline\"\n\nconst bytesExpression = (\n source: NativeAddonSource,\n assetKey: string,\n): string =>\n source === \"sea-asset\"\n ? `Buffer.from(require(\"node:sea\").getAsset(${JSON.stringify(assetKey)}))`\n : `Buffer.from(__gtkxNativeBase64, \"base64\")`\n\nconst shimSource = (\n source: NativeAddonSource,\n assetKey: string,\n base64: string,\n): string => `\nconst { createHash } = require(\"node:crypto\");\nconst { mkdirSync, existsSync, writeFileSync, renameSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\nconst os = require(\"node:os\");\n${source === \"inline\" ? `const __gtkxNativeBase64 = ${JSON.stringify(base64)};` : \"\"}\n\nconst bytes = ${bytesExpression(source, assetKey)};\nconst hash = createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16);\nconst fileName = \"native-\" + hash + \".node\";\n\nconst candidateDirs = [\n join(process.env.XDG_CACHE_HOME || join(os.homedir(), \".cache\"), \"react-native-gtkx-sea\"),\n join(os.tmpdir(), \"react-native-gtkx-sea\"),\n];\n\nlet target;\nlet lastError;\nfor (const dir of candidateDirs) {\n try {\n mkdirSync(dir, { recursive: true });\n const candidate = join(dir, fileName);\n if (!existsSync(candidate)) {\n const tmp = candidate + \".\" + process.pid + \".tmp\";\n writeFileSync(tmp, bytes, { mode: 0o755 });\n renameSync(tmp, candidate);\n }\n target = candidate;\n break;\n } catch (error) {\n lastError = error;\n }\n}\nif (!target) {\n throw new Error(\n \"react-native-gtkx: could not extract the native addon to a writable \" +\n \"cache directory (tried \" + candidateDirs.join(\", \") + \"): \" + String(lastError),\n );\n}\n\n// Not require(target): the require() available to a SEA's main script is\n// restricted to built-ins and embedded assets (verified — requiring an\n// arbitrary absolute path from here throws ERR_UNKNOWN_BUILTIN_MODULE, the\n// same error Node throws for an unrecognized built-in specifier). That\n// restriction doesn't apply to process.dlopen(), the lower-level\n// primitive require() itself uses for \".node\" files (Module._extensions\n// [\".node\"]) — it works on any real path, which is exactly what makes\n// this shim work at all. The \"inline\" build could use a plain require()\n// here, but shares dlopen() rather than branching: one code path that is\n// proven by both artifacts beats two, one of which is rarely exercised.\nconst nativeModule = { exports: {} };\nprocess.dlopen(nativeModule, target);\n// The vite path's own build (dist/bundle.js, gtkx's CLI plugin) calls\n// this immediately after loading the addon, before anything else touches\n// it; @gtkx/runtime's compiled JS never calls it itself (confirmed by\n// inspecting its dist — it only re-exports a handful of low-level\n// functions), so ordinary require() of the addon through Node's own\n// napi module registration must be relying on something dlopen() alone\n// does not trigger. Without this call the addon has no thread registered\n// as \"the Node environment's owner\" and the first GTK-driven callback\n// into JS panics: \"the Node environment was accessed from a thread it is\n// not installed on\" (reproduced and fixed by adding this line).\nnativeModule.exports.init();\nmodule.exports = nativeModule.exports;\n`\n\nexport type NativeAddonShimOptions = {\n /** The bare specifier to replace, in practice \"@gtkx/native\". */\n specifier: string\n /** The app root — the shim's id lives here, see above. */\n appRoot: string\n /** Where the addon's bytes come from at runtime. */\n source: NativeAddonSource\n /** SEA asset key; unused when `source` is \"inline\". */\n assetKey?: string\n /** The addon's bytes; required when `source` is \"inline\". */\n addonBytes?: Buffer\n}\n\n/**\n * Intercepts every import/require of `specifier` (an exact bare module\n * specifier, e.g. \"@gtkx/native\") and replaces it with the extraction shim\n * above. The real module never reaches the bundle graph — only its\n * re-exported surface, loaded from the extracted file at runtime.\n *\n * The shim is given an id inside `appRoot` with a `.cjs` extension rather\n * than a `\\0`-prefixed virtual one: the shim IS CommonJS (it assigns\n * `module.exports` so consumers' NAMED imports of the addon still resolve\n * through the bundler's interop), and a real directory is what lets any\n * specifier inside it resolve normally.\n */\nexport const nativeAddonShimPlugin = (\n options: NativeAddonShimOptions,\n): Plugin => {\n const { specifier, appRoot, source } = options\n if (source === \"inline\" && !options.addonBytes) {\n throw new Error('the \"inline\" native addon source needs addonBytes')\n }\n const shimId = join(appRoot, \"__gtkx-sea-native-shim.cjs\")\n const contents = shimSource(\n source,\n options.assetKey ?? NATIVE_ASSET_KEY,\n options.addonBytes?.toString(\"base64\") ?? \"\",\n )\n return {\n name: \"gtkx-native-addon-shim\",\n resolveId: (id) => (id === specifier ? shimId : null),\n load: (id) => (id === shimId ? contents : null),\n }\n}\n"]}
1
+ {"version":3,"file":"native-shim.js","sourceRoot":"","sources":["../../src/sea/native-shim.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,mEAAmE;AACnE,+CAA+C;AAC/C,wEAAwE;AACxE,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,qBAAqB;AACrB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAgBlD,MAAM,eAAe,GAAG,CACtB,MAAyB,EACzB,QAAgB,EACR,EAAE,CACV,MAAM,KAAK,WAAW;IACpB,CAAC,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;IAC1E,CAAC,CAAC,2CAA2C,CAAA;AAEjD,MAAM,UAAU,GAAG,CACjB,MAAyB,EACzB,QAAgB,EAChB,MAAc,EACN,EAAE,CAAC;;;;;EAKX,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;gBAEpE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDhD,CAAA;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAA+B,EACvB,EAAE;IACV,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAC9C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,UAAU,CACzB,MAAM,EACN,OAAO,CAAC,QAAQ,IAAI,gBAAgB,EACpC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAA;IACD,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;KAChD,CAAA;AACH,CAAC,CAAA","sourcesContent":["// The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon\n// loaded through dlopen — see docs/guide/packaging.md for the shipped\n// behavior, and this file's own comments below for the full design\n// reasoning) cannot be embedded as bundled JS:\n// a Node SEA is a V8 code cache blob, and dlopen needs a real file on a\n// real filesystem, not bytes inside the executable.\n//\n// So it has to be carried as bytes and written back to disk before use.\n// This plugin swaps every import of a given specifier (in practice,\n// \"@gtkx/native\" — see ../sea/bundle.ts) for a shim that does exactly\n// that: extract to a per-user cache directory (content-hash-keyed, so\n// repeat launches reuse the file instead of re-extracting ~1.6 MB every\n// start) and dlopen it from there. Where the bytes come from is the one\n// difference between the two single-file artifacts — Node's SEA \"assets\"\n// mechanism for the executable, a base64 literal for the standalone .cjs;\n// see {@link NativeAddonSource}.\n//\n// Cache location: XDG_CACHE_HOME (or ~/.cache) first, os.tmpdir() as a\n// fallback for a read-only $HOME. Deliberately NOT the SEA executable's\n// own directory — that may be read-only (a mounted image, a root-owned\n// install under /opt) or simply a different place than where the addon\n// was extracted the first time, if the binary moved. This is the same\n// question gtkx's own tutorial answers by placing the addon BESIDE the\n// executable (dist/gtkx.node next to dist/app) — a real divergence, not an\n// oversight: that keeps two files, which is exactly what this epic set\n// out to stop doing.\nimport { join } from \"node:path\"\nimport type { Plugin } from \"rolldown\"\n\nexport const NATIVE_ASSET_KEY = \"gtkx-native.node\"\n\n/**\n * Where the shim gets the addon's bytes from — the only difference between\n * the two single-file artifacts:\n *\n * - `\"sea-asset\"`: from the SEA blob, via node:sea. Only works inside a\n * real single executable.\n * - `\"inline\"`: from a base64 literal in the bundle itself, so the CJS\n * file is self-contained under a plain `node app.cjs` — the same\n * artifact minus the embedded Node runtime. Costs ~4/3 of the addon's\n * size in source text (the addon is ~1.6 MB) and is parsed once at\n * startup; the extraction and dlopen below are shared.\n */\nexport type NativeAddonSource = \"sea-asset\" | \"inline\"\n\nconst bytesExpression = (\n source: NativeAddonSource,\n assetKey: string,\n): string =>\n source === \"sea-asset\"\n ? `Buffer.from(require(\"node:sea\").getAsset(${JSON.stringify(assetKey)}))`\n : `Buffer.from(__gtkxNativeBase64, \"base64\")`\n\nconst shimSource = (\n source: NativeAddonSource,\n assetKey: string,\n base64: string,\n): string => `\nconst { createHash } = require(\"node:crypto\");\nconst { mkdirSync, existsSync, writeFileSync, renameSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\nconst os = require(\"node:os\");\n${source === \"inline\" ? `const __gtkxNativeBase64 = ${JSON.stringify(base64)};` : \"\"}\n\nconst bytes = ${bytesExpression(source, assetKey)};\nconst hash = createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16);\nconst fileName = \"native-\" + hash + \".node\";\n\nconst candidateDirs = [\n join(process.env.XDG_CACHE_HOME || join(os.homedir(), \".cache\"), \"react-native-gtkx-sea\"),\n join(os.tmpdir(), \"react-native-gtkx-sea\"),\n];\n\nlet target;\nlet lastError;\nfor (const dir of candidateDirs) {\n try {\n mkdirSync(dir, { recursive: true });\n const candidate = join(dir, fileName);\n if (!existsSync(candidate)) {\n const tmp = candidate + \".\" + process.pid + \".tmp\";\n writeFileSync(tmp, bytes, { mode: 0o755 });\n renameSync(tmp, candidate);\n }\n target = candidate;\n break;\n } catch (error) {\n lastError = error;\n }\n}\nif (!target) {\n throw new Error(\n \"react-native-gtkx: could not extract the native addon to a writable \" +\n \"cache directory (tried \" + candidateDirs.join(\", \") + \"): \" + String(lastError),\n );\n}\n\n// Not require(target): the require() available to a SEA's main script is\n// restricted to built-ins and embedded assets (verified — requiring an\n// arbitrary absolute path from here throws ERR_UNKNOWN_BUILTIN_MODULE, the\n// same error Node throws for an unrecognized built-in specifier). That\n// restriction doesn't apply to process.dlopen(), the lower-level\n// primitive require() itself uses for \".node\" files (Module._extensions\n// [\".node\"]) — it works on any real path, which is exactly what makes\n// this shim work at all. The \"inline\" build could use a plain require()\n// here, but shares dlopen() rather than branching: one code path that is\n// proven by both artifacts beats two, one of which is rarely exercised.\nconst nativeModule = { exports: {} };\nprocess.dlopen(nativeModule, target);\n// The vite path's own build (dist/bundle.js, gtkx's CLI plugin) calls\n// this immediately after loading the addon, before anything else touches\n// it; @gtkx/runtime's compiled JS never calls it itself (confirmed by\n// inspecting its dist — it only re-exports a handful of low-level\n// functions), so ordinary require() of the addon through Node's own\n// napi module registration must be relying on something dlopen() alone\n// does not trigger. Without this call the addon has no thread registered\n// as \"the Node environment's owner\" and the first GTK-driven callback\n// into JS panics: \"the Node environment was accessed from a thread it is\n// not installed on\" (reproduced and fixed by adding this line).\nnativeModule.exports.init();\nmodule.exports = nativeModule.exports;\n`\n\nexport type NativeAddonShimOptions = {\n /** The bare specifier to replace, in practice \"@gtkx/native\". */\n specifier: string\n /** The app root — the shim's id lives here, see above. */\n appRoot: string\n /** Where the addon's bytes come from at runtime. */\n source: NativeAddonSource\n /** SEA asset key; unused when `source` is \"inline\". */\n assetKey?: string\n /** The addon's bytes; required when `source` is \"inline\". */\n addonBytes?: Buffer\n}\n\n/**\n * Intercepts every import/require of `specifier` (an exact bare module\n * specifier, e.g. \"@gtkx/native\") and replaces it with the extraction shim\n * above. The real module never reaches the bundle graph — only its\n * re-exported surface, loaded from the extracted file at runtime.\n *\n * The shim is given an id inside `appRoot` with a `.cjs` extension rather\n * than a `\\0`-prefixed virtual one: the shim IS CommonJS (it assigns\n * `module.exports` so consumers' NAMED imports of the addon still resolve\n * through the bundler's interop), and a real directory is what lets any\n * specifier inside it resolve normally.\n */\nexport const nativeAddonShimPlugin = (\n options: NativeAddonShimOptions,\n): Plugin => {\n const { specifier, appRoot, source } = options\n if (source === \"inline\" && !options.addonBytes) {\n throw new Error('the \"inline\" native addon source needs addonBytes')\n }\n const shimId = join(appRoot, \"__gtkx-sea-native-shim.cjs\")\n const contents = shimSource(\n source,\n options.assetKey ?? NATIVE_ASSET_KEY,\n options.addonBytes?.toString(\"base64\") ?? \"\",\n )\n return {\n name: \"gtkx-native-addon-shim\",\n resolveId: (id) => (id === specifier ? shimId : null),\n load: (id) => (id === shimId ? contents : null),\n }\n}\n"]}
@@ -41,7 +41,7 @@ export type ReactNativeGtkxTestOptions = {
41
41
  * `test.projects` alongside a portable "unit" project.
42
42
  *
43
43
  * Requires a headless Wayland compositor on PATH (`sway` by default —
44
- * `apt install sway xwayland dbus`, see docs/getting-started.md) and
44
+ * `apt install sway xwayland dbus`, see docs/guide/toolchains.md) and
45
45
  * `gtkx codegen` to have already generated the project's `@gtkx/gi`
46
46
  * bindings — a bare `vitest run` does not trigger codegen itself, unlike
47
47
  * `gtkx dev`/`gtkx build`. A missing compositor or codegen store fails with
@@ -26,7 +26,7 @@ const SETUP_FILE = fileURLToPath(new URL("./setup.js", import.meta.url));
26
26
  * `test.projects` alongside a portable "unit" project.
27
27
  *
28
28
  * Requires a headless Wayland compositor on PATH (`sway` by default —
29
- * `apt install sway xwayland dbus`, see docs/getting-started.md) and
29
+ * `apt install sway xwayland dbus`, see docs/guide/toolchains.md) and
30
30
  * `gtkx codegen` to have already generated the project's `@gtkx/gi`
31
31
  * bindings — a bare `vitest run` does not trigger codegen itself, unlike
32
32
  * `gtkx dev`/`gtkx build`. A missing compositor or codegen store fails with
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vitest/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAC3E,yEAAyE;AACzE,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAA;AAkC/E,MAAM,eAAe,GAAG,CAAC,wBAAwB,CAAC,CAAA;AAClD,MAAM,mBAAmB,GAAwB,CAAC,mBAAmB,CAAC,CAAA;AACtE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAExE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,UAAsC,EAAE,EACV,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;IACnD,MAAM,OAAO,GAAa;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;KAClC,CAAA;IACD,OAAO;QACL,OAAO;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;YAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;YAC3C,UAAU,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE;oBACJ,MAAM,EAAE,CAAC,GAAG,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;iBAChE;aACF;YACD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;SAClD;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,mBAAmB,CAAA","sourcesContent":["// react-native-gtkx/vitest — the vitest recipe this repo's own test suite\n// runs on (see vitest.config.ts's \"gtk\" project), packaged for a consumer\n// app: the @gtkx/vitest headless-compositor plugin, the react-native-gtkx\n// vite preset (react-native alias + Metro-style platform extensions), a\n// default inline-deps list for RN libraries that import \"react-native\"\n// themselves, and the React act-environment setup.\n//\n// Self-contained like ./vite, ./metro and ./runner: a consumer's\n// vitest.config.ts is loaded the same way vite.config.ts is (vite's config\n// loader bundles only the user's own relative import graph; node_modules\n// imports stay bare specifiers, resolved afterwards by plain Node ESM) — so\n// this file must not contain extensionless relative imports. The one\n// relative import below carries an explicit \".js\" extension, which a real\n// sibling file on disk resolves under plain Node without any bundler.\nimport { fileURLToPath, URL } from \"node:url\"\nimport type { PluginOptions as GtkxPluginOptions } from \"@gtkx/vitest\"\nimport type { Plugin } from \"vite\"\nimport type { UserWorkspaceConfig } from \"vitest/config\"\nimport { reactNativeGtkx, type ReactNativeGtkxOptions } from \"../vite/index.js\"\n\n/**\n * Options accepted by {@link reactNativeGtkxTest}. Every field is optional\n * and falls back to the recipe this repo's own test suite runs on.\n */\nexport type ReactNativeGtkxTestOptions = {\n /** Vitest project name. Default: \"gtk\". */\n name?: string\n /** Test file glob(s), relative to the project root. Default: `[\"**\\/*.gtk.test.{ts,tsx}\"]`. */\n include?: string[]\n /** Headless compositor size/compositor, forwarded to @gtkx/vitest's plugin. */\n headless?: GtkxPluginOptions\n /** Metro-style platform resolution options, forwarded to the vite preset. */\n platform?: ReactNativeGtkxOptions\n /**\n * Extra `server.deps.inline` patterns, merged after the default\n * (`/@react-navigation/`). Needed for any node_modules library that\n * itself imports \"react-native\" — Vitest externalizes node_modules code\n * by default, a path the react-native alias cannot reach.\n */\n inlineDeps?: (string | RegExp)[]\n /** Extra setup files, run after the built-in React act-environment setup. */\n setupFiles?: string[]\n /**\n * Run this project's test files serially. Default: false — window-resize\n * signal delivery races when several per-worker headless compositors run\n * at once (see docs/gtkx-rc4-notes.md); the suite this recipe is proven\n * on takes seconds, so serial execution is the cheap fix. Override once a\n * suite is large enough for parallelism to matter more than that race.\n */\n fileParallelism?: boolean\n}\n\nconst DEFAULT_INCLUDE = [\"**/*.gtk.test.{ts,tsx}\"]\nconst DEFAULT_INLINE_DEPS: (string | RegExp)[] = [/@react-navigation/]\nconst SETUP_FILE = fileURLToPath(new URL(\"./setup.js\", import.meta.url))\n\n/**\n * A ready Vitest project config for react-native-gtkx component tests: the\n * headless Wayland compositor, the react-native alias and platform\n * extensions, and the React act-environment setup — the same recipe this\n * repo's own suite runs on. Use the result as a whole config\n * (`defineConfig(reactNativeGtkxTest())`) or as one entry of\n * `test.projects` alongside a portable \"unit\" project.\n *\n * Requires a headless Wayland compositor on PATH (`sway` by default —\n * `apt install sway xwayland dbus`, see docs/getting-started.md) and\n * `gtkx codegen` to have already generated the project's `@gtkx/gi`\n * bindings — a bare `vitest run` does not trigger codegen itself, unlike\n * `gtkx dev`/`gtkx build`. A missing compositor or codegen store fails with\n * a readable error (e.g. `Cannot find the \"sway\" executable on PATH`), not\n * a hang.\n */\nexport const reactNativeGtkxTest = async (\n options: ReactNativeGtkxTestOptions = {},\n): Promise<UserWorkspaceConfig> => {\n const gtkx = (await import(\"@gtkx/vitest\")).default\n const plugins: Plugin[] = [\n gtkx(options.headless),\n reactNativeGtkx(options.platform),\n ]\n return {\n plugins,\n test: {\n name: options.name ?? \"gtk\",\n include: options.include ?? DEFAULT_INCLUDE,\n setupFiles: [SETUP_FILE, ...(options.setupFiles ?? [])],\n server: {\n deps: {\n inline: [...DEFAULT_INLINE_DEPS, ...(options.inlineDeps ?? [])],\n },\n },\n fileParallelism: options.fileParallelism ?? false,\n },\n }\n}\n\nexport default reactNativeGtkxTest\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vitest/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAC3E,yEAAyE;AACzE,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAA;AAkC/E,MAAM,eAAe,GAAG,CAAC,wBAAwB,CAAC,CAAA;AAClD,MAAM,mBAAmB,GAAwB,CAAC,mBAAmB,CAAC,CAAA;AACtE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAExE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,UAAsC,EAAE,EACV,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;IACnD,MAAM,OAAO,GAAa;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;KAClC,CAAA;IACD,OAAO;QACL,OAAO;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;YAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;YAC3C,UAAU,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE;oBACJ,MAAM,EAAE,CAAC,GAAG,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;iBAChE;aACF;YACD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;SAClD;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,mBAAmB,CAAA","sourcesContent":["// react-native-gtkx/vitest — the vitest recipe this repo's own test suite\n// runs on (see vitest.config.ts's \"gtk\" project), packaged for a consumer\n// app: the @gtkx/vitest headless-compositor plugin, the react-native-gtkx\n// vite preset (react-native alias + Metro-style platform extensions), a\n// default inline-deps list for RN libraries that import \"react-native\"\n// themselves, and the React act-environment setup.\n//\n// Self-contained like ./vite, ./metro and ./runner: a consumer's\n// vitest.config.ts is loaded the same way vite.config.ts is (vite's config\n// loader bundles only the user's own relative import graph; node_modules\n// imports stay bare specifiers, resolved afterwards by plain Node ESM) — so\n// this file must not contain extensionless relative imports. The one\n// relative import below carries an explicit \".js\" extension, which a real\n// sibling file on disk resolves under plain Node without any bundler.\nimport { fileURLToPath, URL } from \"node:url\"\nimport type { PluginOptions as GtkxPluginOptions } from \"@gtkx/vitest\"\nimport type { Plugin } from \"vite\"\nimport type { UserWorkspaceConfig } from \"vitest/config\"\nimport { reactNativeGtkx, type ReactNativeGtkxOptions } from \"../vite/index.js\"\n\n/**\n * Options accepted by {@link reactNativeGtkxTest}. Every field is optional\n * and falls back to the recipe this repo's own test suite runs on.\n */\nexport type ReactNativeGtkxTestOptions = {\n /** Vitest project name. Default: \"gtk\". */\n name?: string\n /** Test file glob(s), relative to the project root. Default: `[\"**\\/*.gtk.test.{ts,tsx}\"]`. */\n include?: string[]\n /** Headless compositor size/compositor, forwarded to @gtkx/vitest's plugin. */\n headless?: GtkxPluginOptions\n /** Metro-style platform resolution options, forwarded to the vite preset. */\n platform?: ReactNativeGtkxOptions\n /**\n * Extra `server.deps.inline` patterns, merged after the default\n * (`/@react-navigation/`). Needed for any node_modules library that\n * itself imports \"react-native\" — Vitest externalizes node_modules code\n * by default, a path the react-native alias cannot reach.\n */\n inlineDeps?: (string | RegExp)[]\n /** Extra setup files, run after the built-in React act-environment setup. */\n setupFiles?: string[]\n /**\n * Run this project's test files serially. Default: false — window-resize\n * signal delivery races when several per-worker headless compositors run\n * at once (see docs/gtkx-rc4-notes.md); the suite this recipe is proven\n * on takes seconds, so serial execution is the cheap fix. Override once a\n * suite is large enough for parallelism to matter more than that race.\n */\n fileParallelism?: boolean\n}\n\nconst DEFAULT_INCLUDE = [\"**/*.gtk.test.{ts,tsx}\"]\nconst DEFAULT_INLINE_DEPS: (string | RegExp)[] = [/@react-navigation/]\nconst SETUP_FILE = fileURLToPath(new URL(\"./setup.js\", import.meta.url))\n\n/**\n * A ready Vitest project config for react-native-gtkx component tests: the\n * headless Wayland compositor, the react-native alias and platform\n * extensions, and the React act-environment setup — the same recipe this\n * repo's own suite runs on. Use the result as a whole config\n * (`defineConfig(reactNativeGtkxTest())`) or as one entry of\n * `test.projects` alongside a portable \"unit\" project.\n *\n * Requires a headless Wayland compositor on PATH (`sway` by default —\n * `apt install sway xwayland dbus`, see docs/guide/toolchains.md) and\n * `gtkx codegen` to have already generated the project's `@gtkx/gi`\n * bindings — a bare `vitest run` does not trigger codegen itself, unlike\n * `gtkx dev`/`gtkx build`. A missing compositor or codegen store fails with\n * a readable error (e.g. `Cannot find the \"sway\" executable on PATH`), not\n * a hang.\n */\nexport const reactNativeGtkxTest = async (\n options: ReactNativeGtkxTestOptions = {},\n): Promise<UserWorkspaceConfig> => {\n const gtkx = (await import(\"@gtkx/vitest\")).default\n const plugins: Plugin[] = [\n gtkx(options.headless),\n reactNativeGtkx(options.platform),\n ]\n return {\n plugins,\n test: {\n name: options.name ?? \"gtk\",\n include: options.include ?? DEFAULT_INCLUDE,\n setupFiles: [SETUP_FILE, ...(options.setupFiles ?? [])],\n server: {\n deps: {\n inline: [...DEFAULT_INLINE_DEPS, ...(options.inlineDeps ?? [])],\n },\n },\n fileParallelism: options.fileParallelism ?? false,\n },\n }\n}\n\nexport default reactNativeGtkxTest\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gtkx",
3
- "version": "0.4.0-alpha.2",
3
+ "version": "0.4.0-alpha.3",
4
4
  "description": "React Native components and APIs for Linux desktop, rendered as native GTK4 widgets via gtkx",
5
5
  "license": "MIT",
6
6
  "author": "Anton Petrov <anton@itsmepetrov.com>",