create-honua-app 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +19 -3
- package/templates/react-ts/README.md +11 -11
- package/templates/react-ts/package.json +2 -2
- package/templates/react-ts/src/App.tsx +38 -28
- package/templates/react-ts/src/main.tsx +3 -2
- package/templates/react-ts/src/maplibre-worker.ts +14 -0
- package/templates/vanilla-ts/README.md +5 -4
- package/templates/vanilla-ts/package.json +2 -2
- package/templates/vanilla-ts/src/main.ts +1 -0
- package/templates/vanilla-ts/src/maplibre-worker.ts +14 -0
- package/templates.manifest.json +2 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npm create honua-app@latest my-map -- --template react-ts
|
|
|
33
33
|
| Template | What it shows |
|
|
34
34
|
| --- | --- |
|
|
35
35
|
| `vanilla-ts` | `connect → inspect → explain → query → mount`: the SDK owns the MapLibre map and mounts an accepted query plan. |
|
|
36
|
-
| `react-ts` | The app owns a plain `maplibre-gl` map;
|
|
36
|
+
| `react-ts` | The app owns a plain `maplibre-gl` map; the same kernel connection inspects, explains, queries, and mounts onto it. |
|
|
37
37
|
|
|
38
38
|
Both starters ship a committed GeoServices fixture served by the Vite dev and preview servers, so the green path
|
|
39
39
|
never depends on a third-party endpoint, an account, or an API key. Set `VITE_HONUA_ENDPOINT` to run the same code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-honua-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Scaffold a Honua JavaScript SDK map application: Vite + TypeScript + MapLibre starters that connect and mount a source without hand-assembling peers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Honua",
|
|
@@ -17,7 +17,16 @@
|
|
|
17
17
|
"access": "public",
|
|
18
18
|
"provenance": true
|
|
19
19
|
},
|
|
20
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"gis",
|
|
22
|
+
"geospatial",
|
|
23
|
+
"maplibre",
|
|
24
|
+
"typescript",
|
|
25
|
+
"honua",
|
|
26
|
+
"scaffold",
|
|
27
|
+
"starter",
|
|
28
|
+
"create-app"
|
|
29
|
+
],
|
|
21
30
|
"type": "module",
|
|
22
31
|
"bin": {
|
|
23
32
|
"create-honua-app": "bin/create-honua-app.mjs"
|
|
@@ -25,7 +34,14 @@
|
|
|
25
34
|
"exports": {
|
|
26
35
|
"./package.json": "./package.json"
|
|
27
36
|
},
|
|
28
|
-
"files": [
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"lib",
|
|
40
|
+
"templates",
|
|
41
|
+
"templates.manifest.json",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
29
45
|
"engines": {
|
|
30
46
|
"node": ">=20.19.0"
|
|
31
47
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Honua map app (React + TypeScript)
|
|
2
2
|
|
|
3
3
|
A Vite + React + TypeScript starter for the [Honua JavaScript SDK](https://github.com/honua-io/honua-sdk-js). The app
|
|
4
|
-
owns a plain `maplibre-gl` map and the
|
|
4
|
+
owns a plain `maplibre-gl` map and runs the kernel lifecycle against it:
|
|
5
5
|
|
|
6
6
|
```text
|
|
7
|
-
connect →
|
|
7
|
+
connect → inspect → explain → mount
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
## Run it
|
|
@@ -34,14 +34,14 @@ endpoint or a server-side proxy.
|
|
|
34
34
|
|
|
35
35
|
| File | Why |
|
|
36
36
|
| --- | --- |
|
|
37
|
-
| `src/App.tsx` | Connection, query, and
|
|
38
|
-
| `src/main.tsx` | React root. `StrictMode` stays on:
|
|
37
|
+
| `src/App.tsx` | Connection, accepted query plan, and kernel-owned mount lifecycle. Change the query here. |
|
|
38
|
+
| `src/main.tsx` | React root. `StrictMode` stays on: cancellation and disposal make remounts safe. |
|
|
39
39
|
| `src/fixture-endpoint.ts` | The same-origin path the offline fixture is served on. |
|
|
40
|
+
| `src/maplibre-worker.ts` | MapLibre 6's ESM worker URL, resolved through Vite. Leave it alone unless you swap the renderer. |
|
|
40
41
|
| `vite.config.ts` | The fixture service. Delete it once you point the app at a real endpoint. |
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
compiles the JSX without it.
|
|
43
|
+
The starter passes its app-owned map directly to `connection.mount` and disposes the kernel when React tears the
|
|
44
|
+
effect down. Add `@vitejs/plugin-react` if you want React Fast Refresh — Vite compiles the JSX without it.
|
|
45
45
|
|
|
46
46
|
## Dependencies
|
|
47
47
|
|
|
@@ -49,10 +49,10 @@ compiles the JSX without it.
|
|
|
49
49
|
`@connectrpc` packages are the SDK's optional transport peers: they are installed and pinned here so the bundle builds
|
|
50
50
|
without any peer-dependency assembly, and they are what a Honua-server connection uses.
|
|
51
51
|
|
|
52
|
-
This starter pins **MapLibre GL JS
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
This starter pins **MapLibre GL JS 6.1.0**, which the published `@honua/sdk-js` release accepts as an optional peer
|
|
53
|
+
(`^5.0.0 || ^6.0.0`). MapLibre 6 is ESM-only and loads its worker as a separate module, so `src/maplibre-worker.ts`
|
|
54
|
+
calls `setWorkerUrl` with Vite's `?worker&url` import before the first map is created; `src/main.tsx` imports it
|
|
55
|
+
first. Downgrading to MapLibre 5 is supported by the SDK and means deleting that module and its import.
|
|
56
56
|
|
|
57
57
|
## Checks
|
|
58
58
|
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@bufbuild/protobuf": "2.13.0",
|
|
17
17
|
"@connectrpc/connect": "2.1.2",
|
|
18
18
|
"@connectrpc/connect-web": "2.1.2",
|
|
19
|
-
"@honua/sdk-js": "0.1.
|
|
20
|
-
"maplibre-gl": "
|
|
19
|
+
"@honua/sdk-js": "0.1.10-beta.0",
|
|
20
|
+
"maplibre-gl": "6.1.0",
|
|
21
21
|
"react": "19.2.8",
|
|
22
22
|
"react-dom": "19.2.8"
|
|
23
23
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHonua } from "@honua/sdk-js";
|
|
2
|
-
import type { ConnectProtocolHint,
|
|
3
|
-
import {
|
|
2
|
+
import type { ConnectProtocolHint, RendererDiagnostic } from "@honua/sdk-js";
|
|
3
|
+
import { maplibreRenderer } from "@honua/sdk-js/runtime";
|
|
4
4
|
import * as maplibregl from "maplibre-gl";
|
|
5
5
|
import type { RefObject } from "react";
|
|
6
6
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -34,9 +34,10 @@ const PROTOCOL = (
|
|
|
34
34
|
const DATA_LANE = configuredEndpoint && configuredEndpoint.length > 0 ? "Live endpoint" : "Committed fixture";
|
|
35
35
|
|
|
36
36
|
interface ConnectionState {
|
|
37
|
-
readonly source: Source | null;
|
|
38
37
|
readonly sourceId?: string;
|
|
39
38
|
readonly protocol?: string;
|
|
39
|
+
readonly diagnostics?: readonly RendererDiagnostic[];
|
|
40
|
+
readonly mounted: boolean;
|
|
40
41
|
readonly error?: string;
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -65,54 +66,67 @@ function useExternalMap(): { containerRef: RefObject<HTMLDivElement | null>; map
|
|
|
65
66
|
return { containerRef, map };
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
/**
|
|
69
|
-
function
|
|
70
|
-
const [state, setState] = useState<ConnectionState>({
|
|
69
|
+
/** Run the canonical kernel-owned lifecycle against the app-owned map. */
|
|
70
|
+
function useHonuaMap(map: maplibregl.Map | null): ConnectionState {
|
|
71
|
+
const [state, setState] = useState<ConnectionState>({ mounted: false });
|
|
71
72
|
|
|
72
73
|
useEffect(() => {
|
|
74
|
+
if (!map) return;
|
|
73
75
|
const honua = createHonua();
|
|
74
|
-
|
|
76
|
+
const cancellation = new AbortController();
|
|
75
77
|
void (async () => {
|
|
76
78
|
try {
|
|
77
79
|
const connection = await honua.connect(
|
|
78
80
|
{ url: ENDPOINT, protocol: PROTOCOL },
|
|
79
|
-
{ authorizationScopeFingerprint: "anonymous-public" },
|
|
81
|
+
{ authorizationScopeFingerprint: "anonymous-public", signal: cancellation.signal },
|
|
80
82
|
);
|
|
81
|
-
const inspection = await connection.inspect();
|
|
83
|
+
const inspection = await connection.inspect({ signal: cancellation.signal });
|
|
82
84
|
const sourceId = inspection.defaultSourceId ?? inspection.sources[0]?.descriptor.id;
|
|
83
85
|
if (!sourceId) throw new Error("The endpoint advertised no queryable source.");
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
const plan = await connection.explain(QUERY, { sourceId, signal: cancellation.signal });
|
|
87
|
+
const mounted = await connection.mount(map, {
|
|
88
|
+
renderer: maplibreRenderer(maplibregl),
|
|
89
|
+
query: plan,
|
|
90
|
+
sourceId,
|
|
91
|
+
signal: cancellation.signal,
|
|
92
|
+
});
|
|
93
|
+
await mounted.ready;
|
|
94
|
+
setState({
|
|
95
|
+
sourceId,
|
|
96
|
+
protocol: inspection.protocol,
|
|
97
|
+
diagnostics: mounted.diagnostics,
|
|
98
|
+
mounted: true,
|
|
99
|
+
});
|
|
86
100
|
} catch (error) {
|
|
87
|
-
if (!
|
|
101
|
+
if (!cancellation.signal.aborted) {
|
|
102
|
+
setState({ mounted: false, error: error instanceof Error ? error.message : String(error) });
|
|
103
|
+
}
|
|
88
104
|
}
|
|
89
105
|
})();
|
|
90
106
|
return () => {
|
|
91
|
-
|
|
107
|
+
cancellation.abort();
|
|
92
108
|
void honua.dispose();
|
|
93
109
|
};
|
|
94
|
-
}, []);
|
|
110
|
+
}, [map]);
|
|
95
111
|
|
|
96
112
|
return state;
|
|
97
113
|
}
|
|
98
114
|
|
|
99
115
|
export function App() {
|
|
100
116
|
const { containerRef, map } = useExternalMap();
|
|
101
|
-
const connection =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
: mounted.handle
|
|
108
|
-
? "The queried source is mounted through the React bridge."
|
|
117
|
+
const connection = useHonuaMap(map);
|
|
118
|
+
|
|
119
|
+
const status = connection.error
|
|
120
|
+
? `The workflow stopped: ${connection.error}`
|
|
121
|
+
: connection.mounted
|
|
122
|
+
? "The accepted plan is mounted through the kernel lifecycle."
|
|
109
123
|
: "Connecting, discovering, and mounting…";
|
|
110
124
|
|
|
111
125
|
return (
|
|
112
126
|
<main className="app">
|
|
113
127
|
<section className="panel" aria-label="Workflow">
|
|
114
128
|
<p className="eyebrow">Honua JavaScript SDK · React</p>
|
|
115
|
-
<h1 className="title">connect →
|
|
129
|
+
<h1 className="title">connect → inspect → explain → query → mount</h1>
|
|
116
130
|
<output className="status" aria-live="polite">
|
|
117
131
|
{status}
|
|
118
132
|
</output>
|
|
@@ -135,11 +149,7 @@ export function App() {
|
|
|
135
149
|
</div>
|
|
136
150
|
<div>
|
|
137
151
|
<dt>Strategy</dt>
|
|
138
|
-
<dd>{
|
|
139
|
-
</div>
|
|
140
|
-
<div>
|
|
141
|
-
<dt>Features</dt>
|
|
142
|
-
<dd id="fact-features">{mounted.diagnostics?.featureCount ?? "—"}</dd>
|
|
152
|
+
<dd>{connection.diagnostics?.find((item) => item.strategy)?.strategy ?? "—"}</dd>
|
|
143
153
|
</div>
|
|
144
154
|
</dl>
|
|
145
155
|
<p className="hint">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./maplibre-worker.js";
|
|
1
2
|
import "maplibre-gl/dist/maplibre-gl.css";
|
|
2
3
|
|
|
3
4
|
import { StrictMode } from "react";
|
|
@@ -9,8 +10,8 @@ import "./styles.css";
|
|
|
9
10
|
const container = document.getElementById("root");
|
|
10
11
|
if (!container) throw new Error("Missing #root element");
|
|
11
12
|
|
|
12
|
-
// StrictMode double-invokes effects in development.
|
|
13
|
-
//
|
|
13
|
+
// StrictMode double-invokes effects in development. App owns cancellation and
|
|
14
|
+
// SDK disposal, so the mount/unmount/mount cycle never leaks a map source.
|
|
14
15
|
createRoot(container).render(
|
|
15
16
|
<StrictMode>
|
|
16
17
|
<App />
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// MapLibre GL JS 6 is ESM-only and loads its worker as a separate module,
|
|
2
|
+
// resolving `./maplibre-gl-worker.mjs` against its own `import.meta.url`. That
|
|
3
|
+
// URL survives a dev server but not a production bundle, where MapLibre's own
|
|
4
|
+
// module has been rewritten into an app chunk that sits nowhere near the
|
|
5
|
+
// installed package and its sibling worker file. Vite's
|
|
6
|
+
// `?worker&url` pipeline emits the worker as a real asset and hands back the
|
|
7
|
+
// hashed URL it was emitted to, which is what `setWorkerUrl` needs.
|
|
8
|
+
//
|
|
9
|
+
// Import this module before the first map is created; `src/main.tsx` does.
|
|
10
|
+
|
|
11
|
+
import { setWorkerUrl } from "maplibre-gl";
|
|
12
|
+
import maplibreWorkerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";
|
|
13
|
+
|
|
14
|
+
setWorkerUrl(maplibreWorkerUrl);
|
|
@@ -36,6 +36,7 @@ endpoint or a server-side proxy.
|
|
|
36
36
|
| --- | --- |
|
|
37
37
|
| `src/main.ts` | The whole workflow: connect, inspect, explain, query, mount. Change the query here. |
|
|
38
38
|
| `src/fixture-endpoint.ts` | The same-origin path the offline fixture is served on. |
|
|
39
|
+
| `src/maplibre-worker.ts` | MapLibre 6's ESM worker URL, resolved through Vite. Leave it alone unless you swap the renderer. |
|
|
39
40
|
| `vite.config.ts` | The fixture service. Delete it once you point the app at a real endpoint. |
|
|
40
41
|
| `fixtures/` | The committed GeoServices layer description and its query response. |
|
|
41
42
|
|
|
@@ -45,10 +46,10 @@ endpoint or a server-side proxy.
|
|
|
45
46
|
`@connectrpc` packages are the SDK's optional transport peers: they are installed and pinned here so the bundle builds
|
|
46
47
|
without any peer-dependency assembly, and they are what a Honua-server connection uses.
|
|
47
48
|
|
|
48
|
-
This starter pins **MapLibre GL JS
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
This starter pins **MapLibre GL JS 6.1.0**, which the published `@honua/sdk-js` release accepts as an optional peer
|
|
50
|
+
(`^5.0.0 || ^6.0.0`). MapLibre 6 is ESM-only and loads its worker as a separate module, so `src/maplibre-worker.ts`
|
|
51
|
+
calls `setWorkerUrl` with Vite's `?worker&url` import before the first map is created; `src/main.ts` imports it first.
|
|
52
|
+
Downgrading to MapLibre 5 is supported by the SDK and means deleting that module and its import.
|
|
52
53
|
|
|
53
54
|
## Checks
|
|
54
55
|
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@bufbuild/protobuf": "2.13.0",
|
|
17
17
|
"@connectrpc/connect": "2.1.2",
|
|
18
18
|
"@connectrpc/connect-web": "2.1.2",
|
|
19
|
-
"@honua/sdk-js": "0.1.
|
|
20
|
-
"maplibre-gl": "
|
|
19
|
+
"@honua/sdk-js": "0.1.10-beta.0",
|
|
20
|
+
"maplibre-gl": "6.1.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "24.13.3",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// MapLibre GL JS 6 is ESM-only and loads its worker as a separate module,
|
|
2
|
+
// resolving `./maplibre-gl-worker.mjs` against its own `import.meta.url`. That
|
|
3
|
+
// URL survives a dev server but not a production bundle, where MapLibre's own
|
|
4
|
+
// module has been rewritten into an app chunk that sits nowhere near the
|
|
5
|
+
// installed package and its sibling worker file. Vite's
|
|
6
|
+
// `?worker&url` pipeline emits the worker as a real asset and hands back the
|
|
7
|
+
// hashed URL it was emitted to, which is what `setWorkerUrl` needs.
|
|
8
|
+
//
|
|
9
|
+
// Import this module before the first map is created; `src/main.ts` does.
|
|
10
|
+
|
|
11
|
+
import { setWorkerUrl } from "maplibre-gl";
|
|
12
|
+
import maplibreWorkerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";
|
|
13
|
+
|
|
14
|
+
setWorkerUrl(maplibreWorkerUrl);
|
package/templates.manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"schemaVersion": 1,
|
|
4
4
|
"sdk": {
|
|
5
5
|
"package": "@honua/sdk-js",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.10-beta.0"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"owner": "honua-io",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{
|
|
43
43
|
"id": "react-ts",
|
|
44
44
|
"title": "React TypeScript",
|
|
45
|
-
"summary": "Vite + React + TypeScript app that mounts
|
|
45
|
+
"summary": "Vite + React + TypeScript app that mounts one accepted plan through the SDK kernel on an app-owned MapLibre map.",
|
|
46
46
|
"path": "packages/create-honua-app/templates/react-ts",
|
|
47
47
|
"entryFile": "src/App.tsx",
|
|
48
48
|
"dataLane": "fixture",
|