@junejs/server 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/migrate.ts +18 -0
- package/src/pipeline.ts +23 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "June host adapters + dev server. Host-coupled; depends on the pure @junejs/core contract layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"typecheck": "tsc --noEmit"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@junejs/core": "0.0.
|
|
34
|
-
"@junejs/db": "0.0.
|
|
33
|
+
"@junejs/core": "0.0.16",
|
|
34
|
+
"@junejs/db": "0.0.16",
|
|
35
35
|
"marked": "^18.0.5",
|
|
36
36
|
"rolldown": "^1.1.0",
|
|
37
37
|
"postcss": "^8.4.0",
|
package/src/index.ts
CHANGED
package/src/migrate.ts
CHANGED
|
@@ -112,6 +112,24 @@ export async function migrateApp(
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// `june db types`: open the declared db, bring it to head (safe migrations only, so
|
|
116
|
+
// the schema is present even on a fresh checkout), then hand it to the data layer's
|
|
117
|
+
// codegen hook for the type-declaration text. Returns null when there's no db or the
|
|
118
|
+
// data layer doesn't generate types — the CLI owns the messaging. Introspection is
|
|
119
|
+
// read-only; the safe migrate is idempotent (the ledger skips applied ones).
|
|
120
|
+
export async function typesApp(root: string, config: JuneConfig): Promise<string | null> {
|
|
121
|
+
const factory = config.resources?.db;
|
|
122
|
+
const emit = config.dataLayer?.emitTypes;
|
|
123
|
+
if (!factory || !emit) return null;
|
|
124
|
+
const db = await factory.open();
|
|
125
|
+
try {
|
|
126
|
+
await migrate(db, join(root, "db", "migrations"), { allowDestructive: false });
|
|
127
|
+
return await emit(db);
|
|
128
|
+
} finally {
|
|
129
|
+
await db.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
115
133
|
// The "this migration was gated" guidance, shared by dev (warns, keeps serving)
|
|
116
134
|
// and `june db migrate` (exits non-zero).
|
|
117
135
|
export function blockedMessage(m: ClassifiedMigration): string {
|
package/src/pipeline.ts
CHANGED
|
@@ -124,25 +124,6 @@ function StreamedView({
|
|
|
124
124
|
return provideLoaderData(data, def.view ? def.view(data, ctx) : null);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
// renderToReadableStream does not emit the doctype; prepend it without buffering
|
|
128
|
-
// the React stream (streaming stays incremental).
|
|
129
|
-
function withDoctype(source: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {
|
|
130
|
-
const reader = source.getReader();
|
|
131
|
-
return new ReadableStream<Uint8Array>({
|
|
132
|
-
start(controller) {
|
|
133
|
-
controller.enqueue(new TextEncoder().encode("<!doctype html>\n"));
|
|
134
|
-
},
|
|
135
|
-
async pull(controller) {
|
|
136
|
-
const { done, value } = await reader.read();
|
|
137
|
-
if (done) controller.close();
|
|
138
|
-
else controller.enqueue(value);
|
|
139
|
-
},
|
|
140
|
-
cancel(reason) {
|
|
141
|
-
void reader.cancel(reason);
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
127
|
// The default favicon: the site name's first character in a rounded square —
|
|
147
128
|
// a plain SVG string, so it needs no fonts, no rasterizer, and works for CJK
|
|
148
129
|
// names as readily as latin ones. Served at /favicon.svg AND /favicon.ico
|
|
@@ -187,8 +168,9 @@ export function createPipeline(cfg: PipelineConfig): Pipeline {
|
|
|
187
168
|
React.createElement(Document, { config: docConfigForRender(), metadata, children: wrapped }),
|
|
188
169
|
);
|
|
189
170
|
await stream.allReady; // fully resolved markup (no streamed Suspense fallbacks)
|
|
190
|
-
|
|
191
|
-
|
|
171
|
+
// React 19 emits <!DOCTYPE html> itself for an <html> root — don't prepend a
|
|
172
|
+
// second one.
|
|
173
|
+
return new Response(stream, { status, headers: htmlHeaders() });
|
|
192
174
|
}
|
|
193
175
|
|
|
194
176
|
// Route A: the [data-june-root] inner HTML for a soft-nav / live-apply request
|
|
@@ -249,8 +231,9 @@ export function createPipeline(cfg: PipelineConfig): Pipeline {
|
|
|
249
231
|
React.createElement(Document, { config: docConfigForRender(), metadata, children: wrapped }),
|
|
250
232
|
{ onError: (e: unknown) => console.error("[june] streaming render error:", e) },
|
|
251
233
|
);
|
|
252
|
-
// NO allReady — return the live stream (shell first)
|
|
253
|
-
|
|
234
|
+
// NO allReady — return the live stream (shell first). React 19 streams the
|
|
235
|
+
// <!DOCTYPE html> as the first bytes, so nothing to prepend.
|
|
236
|
+
return new Response(stream, { status: 200, headers: htmlHeaders() });
|
|
254
237
|
}
|
|
255
238
|
|
|
256
239
|
function notFoundResponse(target: RenderTarget, pathname: string): Promise<Response> | Response {
|
|
@@ -300,6 +283,23 @@ export function createPipeline(cfg: PipelineConfig): Pipeline {
|
|
|
300
283
|
return notFoundResponse(target, ctx.url.pathname);
|
|
301
284
|
}
|
|
302
285
|
|
|
286
|
+
const res = await renderTarget(target, def, data, ctx, chain);
|
|
287
|
+
// Every projection here is chosen by the Accept header at the SAME url (a full
|
|
288
|
+
// page, its fragment, its .md and .json all live at the clean path). Without
|
|
289
|
+
// Vary, a shared/browser cache can hand a soft-nav fragment to a real page
|
|
290
|
+
// load — which surfaces as a document that starts mid-body. Make Accept part
|
|
291
|
+
// of the cache key.
|
|
292
|
+
res.headers.set("vary", "accept");
|
|
293
|
+
return res;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function renderTarget(
|
|
297
|
+
target: RenderTarget,
|
|
298
|
+
def: Resolved["def"],
|
|
299
|
+
data: unknown,
|
|
300
|
+
ctx: RouteContext,
|
|
301
|
+
chain: LayoutComponent[],
|
|
302
|
+
): Promise<Response> {
|
|
303
303
|
if (target === "md") return renderMarkdown(def, data, ctx);
|
|
304
304
|
if (target === "json") {
|
|
305
305
|
// Convention: a json() fn customizes; absent → serialize the loader data.
|