gemi 0.49.0-rc.0 → 0.49.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/app/index.js +1 -1
  2. package/dist/bin/gemi.js +251 -19
  3. package/dist/bin/gemi.js.map +10 -5
  4. package/dist/chunk-ff8gkrsr.js +5 -0
  5. package/dist/{chunk-smsjrnb1.js.map → chunk-ff8gkrsr.js.map} +3 -3
  6. package/dist/{chunk-r64ba1pf.js → chunk-qz5jz43n.js} +1 -1
  7. package/dist/chunk-r3hacxdr.js +79 -0
  8. package/dist/{chunk-d9k245jt.js.map → chunk-r3hacxdr.js.map} +9 -5
  9. package/dist/{chunk-g5006g1v.js → chunk-thwfndp3.js} +2 -2
  10. package/dist/{chunk-g5006g1v.js.map → chunk-thwfndp3.js.map} +1 -1
  11. package/dist/chunk-wetdcmvf.js +55 -0
  12. package/dist/{chunk-qjer8mp2.js.map → chunk-wetdcmvf.js.map} +3 -3
  13. package/dist/chunk-x5f4e2fp.js +4 -0
  14. package/dist/{chunk-evkv0xa7.js.map → chunk-x5f4e2fp.js.map} +3 -3
  15. package/dist/client/ClientRouter.d.ts +2 -0
  16. package/dist/client/ClientRouter.d.ts.map +1 -1
  17. package/dist/client/ComponentContext.d.ts +6 -0
  18. package/dist/client/ComponentContext.d.ts.map +1 -1
  19. package/dist/client/QueryManagerContext.d.ts.map +1 -1
  20. package/dist/client/ServerQueryContext.d.ts +25 -0
  21. package/dist/client/ServerQueryContext.d.ts.map +1 -0
  22. package/dist/client/createRoot.d.ts.map +1 -1
  23. package/dist/client/index.js +51 -9
  24. package/dist/client/index.js.map +1 -1
  25. package/dist/client/useQuery.d.ts.map +1 -1
  26. package/dist/facades/Prefetch.d.ts +18 -13
  27. package/dist/facades/Prefetch.d.ts.map +1 -1
  28. package/dist/facades/index.js +1 -1
  29. package/dist/http/HttpRequest.d.ts +1 -2
  30. package/dist/http/HttpRequest.d.ts.map +1 -1
  31. package/dist/http/index.js +1 -1
  32. package/dist/http/requestContext.d.ts +7 -2
  33. package/dist/http/requestContext.d.ts.map +1 -1
  34. package/dist/i18n/index.js +1 -1
  35. package/dist/kernel/index.js +1 -1
  36. package/dist/server/httpDev.d.ts.map +1 -1
  37. package/dist/server/httpProd.d.ts.map +1 -1
  38. package/dist/server/index.js +1 -1
  39. package/dist/services/router/ServerQueryStore.d.ts +80 -0
  40. package/dist/services/router/ServerQueryStore.d.ts.map +1 -0
  41. package/dist/services/router/ViewRouterServiceContainer.d.ts +6 -0
  42. package/dist/services/router/ViewRouterServiceContainer.d.ts.map +1 -1
  43. package/dist/services/router/serverQueryFetcher.d.ts +12 -0
  44. package/dist/services/router/serverQueryFetcher.d.ts.map +1 -0
  45. package/dist/services/router/streamQueryInjection.d.ts +36 -0
  46. package/dist/services/router/streamQueryInjection.d.ts.map +1 -0
  47. package/package.json +1 -1
  48. package/dist/chunk-d9k245jt.js +0 -79
  49. package/dist/chunk-evkv0xa7.js +0 -4
  50. package/dist/chunk-qjer8mp2.js +0 -55
  51. package/dist/chunk-smsjrnb1.js +0 -5
  52. /package/dist/{chunk-r64ba1pf.js.map → chunk-qz5jz43n.js.map} +0 -0
package/dist/app/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{o as a,p as b}from"../chunk-r64ba1pf.js";import"../chunk-d9k245jt.js";import"../chunk-evkv0xa7.js";import"../chunk-p5r0t9jh.js";import"../chunk-xkrthw08.js";import"../chunk-4z53shav.js";import"../chunk-m0earq80.js";import"../chunk-f18weffa.js";export{b as prismaExtension,a as App};
2
+ import{o as a,p as b}from"../chunk-qz5jz43n.js";import"../chunk-r3hacxdr.js";import"../chunk-x5f4e2fp.js";import"../chunk-p5r0t9jh.js";import"../chunk-xkrthw08.js";import"../chunk-4z53shav.js";import"../chunk-m0earq80.js";import"../chunk-f18weffa.js";export{b as prismaExtension,a as App};
3
3
 
4
4
  //# debugId=15EBDBCE0B2D702364756E2164756E21
package/dist/bin/gemi.js CHANGED
@@ -50142,8 +50142,7 @@ class Store {
50142
50142
  req;
50143
50143
  cookies = new Set;
50144
50144
  headers = new Headers;
50145
- prefetchedResources = new Map;
50146
- prefetchPromiseQueue = new Set;
50145
+ serverQueries = null;
50147
50146
  user = null;
50148
50147
  csrfHmac = null;
50149
50148
  locale = null;
@@ -50179,8 +50178,7 @@ class Store {
50179
50178
  destroy() {
50180
50179
  delete this.cookies;
50181
50180
  delete this.headers;
50182
- delete this.prefetchedResources;
50183
- delete this.prefetchPromiseQueue;
50181
+ this.serverQueries = null;
50184
50182
  delete this.user;
50185
50183
  }
50186
50184
  }
@@ -51521,6 +51519,160 @@ class I18n {
51521
51519
  }
51522
51520
  // facades/FileStorage.ts
51523
51521
  import sharp from "sharp";
51522
+ // utils/omitNullishValues.ts
51523
+ function omitNullishValues(input) {
51524
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => {
51525
+ return value !== null && value !== undefined;
51526
+ }));
51527
+ }
51528
+
51529
+ // services/router/ServerQueryStore.ts
51530
+ var LATE_DISCOVERY_THRESHOLD_MS = 50;
51531
+
51532
+ class ServerQueryStore {
51533
+ fetcher;
51534
+ entries = new Map;
51535
+ settleListener = null;
51536
+ settledBacklog = [];
51537
+ shipped = new Set;
51538
+ requestStartedAt = performance.now();
51539
+ renderStartedAt = null;
51540
+ constructor(fetcher) {
51541
+ this.fetcher = fetcher;
51542
+ }
51543
+ elapsed() {
51544
+ return Math.round(performance.now() - this.requestStartedAt);
51545
+ }
51546
+ keyFor(path, variantKey) {
51547
+ return [path, variantKey].filter((s2) => s2.length > 0).join("?");
51548
+ }
51549
+ markRenderStart() {
51550
+ if (this.renderStartedAt === null) {
51551
+ this.renderStartedAt = performance.now();
51552
+ }
51553
+ }
51554
+ read(path, variantKey) {
51555
+ return this.entries.get(this.keyFor(path, variantKey));
51556
+ }
51557
+ ensure(patternPath, options = {}, source = "render") {
51558
+ const params = options.params ?? {};
51559
+ const searchParams = new URLSearchParams(omitNullishValues(options.search ?? {}));
51560
+ searchParams.delete("json");
51561
+ searchParams.sort();
51562
+ const path = applyParams(patternPath, params);
51563
+ const variantKey = searchParams.toString();
51564
+ const key = this.keyFor(path, variantKey);
51565
+ const existing = this.entries.get(key);
51566
+ if (existing)
51567
+ return existing;
51568
+ if (source === "render" && this.renderStartedAt !== null && true) {
51569
+ const delay = Math.round(performance.now() - this.renderStartedAt);
51570
+ if (delay > LATE_DISCOVERY_THRESHOLD_MS) {
51571
+ const searchHint = variantKey ? `, { search: ${JSON.stringify(Object.fromEntries(searchParams))} }` : "";
51572
+ console.warn(`[gemi] useQuery("${patternPath}") started ${delay}ms into the server render \u2014 ` + `it was discovered under a suspended segment, so its fetch could not overlap ` + `the others. It still streams, but \`Query.prefetch("${patternPath}"${searchHint})\` ` + `in the route's view handler would start it at request time.`);
51573
+ }
51574
+ }
51575
+ let settle;
51576
+ const promise = new Promise((resolve) => {
51577
+ settle = resolve;
51578
+ });
51579
+ const entry = {
51580
+ path,
51581
+ variantKey,
51582
+ status: "pending",
51583
+ promise,
51584
+ source,
51585
+ startedAt: this.elapsed()
51586
+ };
51587
+ this.entries.set(key, entry);
51588
+ this.fetcher(patternPath, params, searchParams).then((data) => {
51589
+ entry.status = "resolved";
51590
+ entry.data = data;
51591
+ }, (error) => {
51592
+ entry.status = "rejected";
51593
+ entry.error = error;
51594
+ }).then(() => {
51595
+ entry.settledAt = this.elapsed();
51596
+ if (this.settleListener) {
51597
+ this.settleListener(entry);
51598
+ } else {
51599
+ this.settledBacklog.push(entry);
51600
+ }
51601
+ settle();
51602
+ });
51603
+ return entry;
51604
+ }
51605
+ async allSettled() {
51606
+ let count = 0;
51607
+ while (count !== this.entries.size) {
51608
+ count = this.entries.size;
51609
+ await Promise.all(Array.from(this.entries.values(), (e2) => e2.promise));
51610
+ }
51611
+ }
51612
+ snapshotResolved() {
51613
+ const snapshot = {};
51614
+ for (const [key, entry] of this.entries) {
51615
+ if (entry.status !== "resolved")
51616
+ continue;
51617
+ this.shipped.add(key);
51618
+ snapshot[entry.path] ??= {};
51619
+ snapshot[entry.path][entry.variantKey] = entry.data;
51620
+ }
51621
+ return snapshot;
51622
+ }
51623
+ onSettle(listener) {
51624
+ this.settleListener = (entry) => {
51625
+ if (this.shipped.has(this.keyFor(entry.path, entry.variantKey)))
51626
+ return;
51627
+ listener(entry);
51628
+ };
51629
+ const backlog = this.settledBacklog;
51630
+ this.settledBacklog = [];
51631
+ for (const entry of backlog) {
51632
+ this.settleListener(entry);
51633
+ }
51634
+ }
51635
+ }
51636
+
51637
+ // client/QueryError.ts
51638
+ class QueryError extends Error {
51639
+ path;
51640
+ variantKey;
51641
+ status;
51642
+ body;
51643
+ constructor(path, variantKey, status, body) {
51644
+ super(typeof body?.message === "string" ? body.message : `Request to /api${path} failed with status ${status}`);
51645
+ this.path = path;
51646
+ this.variantKey = variantKey;
51647
+ this.status = status;
51648
+ this.body = body;
51649
+ this.name = "QueryError";
51650
+ }
51651
+ }
51652
+
51653
+ // services/router/serverQueryFetcher.ts
51654
+ function createServerQueryFetcher(req) {
51655
+ const kernelStore = kernelContext.getStore();
51656
+ return (patternPath, params, searchParams) => {
51657
+ const origin = new URL(req.url).origin;
51658
+ const pathnameWithSearch = [applyParams(patternPath, params), searchParams.toString()].filter((s2) => s2.length > 0).join("?");
51659
+ const newReq = new Request(`${origin}/${pathnameWithSearch}`, {
51660
+ headers: req.headers
51661
+ });
51662
+ const httpRequest = new HttpRequest(newReq, params);
51663
+ return kernelContext.run(kernelStore, () => RequestContext.run(httpRequest, async () => {
51664
+ const data = await ApiRouterServiceContainer.use().getRouteData(patternPath);
51665
+ if (data instanceof Response) {
51666
+ const body = await data.json().catch(() => null);
51667
+ if (!data.ok) {
51668
+ throw new QueryError(applyParams(patternPath, params), searchParams.toString(), data.status, body);
51669
+ }
51670
+ return body;
51671
+ }
51672
+ return data;
51673
+ }));
51674
+ };
51675
+ }
51524
51676
  // services/logging/LoggingServiceContainer.ts
51525
51677
  import { mkdir, readdir, access } from "fs/promises";
51526
51678
  async function exists(path) {
@@ -68327,7 +68479,60 @@ class KernelIdServiceContainer extends ServiceContainer {
68327
68479
  }
68328
68480
  }
68329
68481
 
68482
+ // services/router/streamQueryInjection.ts
68483
+ function htmlSafeJson(value) {
68484
+ return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
68485
+ }
68486
+ function queryPayloadScript(entry) {
68487
+ const payload = htmlSafeJson([entry.path, entry.variantKey, entry.data]);
68488
+ return `<script>(self.__GEMI_STREAM__=self.__GEMI_STREAM__||[]).push(${payload})</script>`;
68489
+ }
68490
+ function injectQueryPayloads(source, store) {
68491
+ const encoder = new TextEncoder;
68492
+ const queue = [];
68493
+ let sentFirstChunk = false;
68494
+ store.onSettle((entry) => {
68495
+ if (entry.status !== "resolved")
68496
+ return;
68497
+ queue.push(queryPayloadScript(entry));
68498
+ });
68499
+ const reader = source.getReader();
68500
+ const flush = (controller) => {
68501
+ while (queue.length > 0) {
68502
+ controller.enqueue(encoder.encode(queue.shift()));
68503
+ }
68504
+ };
68505
+ return new ReadableStream({
68506
+ async pull(controller) {
68507
+ const { done, value } = await reader.read();
68508
+ if (done) {
68509
+ flush(controller);
68510
+ controller.close();
68511
+ return;
68512
+ }
68513
+ if (!sentFirstChunk) {
68514
+ sentFirstChunk = true;
68515
+ controller.enqueue(value);
68516
+ flush(controller);
68517
+ return;
68518
+ }
68519
+ flush(controller);
68520
+ controller.enqueue(value);
68521
+ },
68522
+ cancel(reason) {
68523
+ return reader.cancel(reason);
68524
+ }
68525
+ });
68526
+ }
68527
+ var BOT_UA = /bot|crawler|spider|crawling|slurp|bingpreview|mediapartners|headless|lighthouse|prerender|facebookexternalhit|whatsapp|telegram|discord|twitterbot|linkedinbot/i;
68528
+ function isBotUserAgent(userAgent) {
68529
+ if (!userAgent)
68530
+ return false;
68531
+ return BOT_UA.test(userAgent);
68532
+ }
68533
+
68330
68534
  // services/router/ViewRouterServiceContainer.ts
68535
+ var STREAM_DEADLINE_MS = 1e4;
68331
68536
  var themeScript = `
68332
68537
  !function(){try{var d=document.documentElement,c=d.classList;
68333
68538
  c.remove('light','dark');
@@ -68404,7 +68609,8 @@ class ViewRouterServiceContainer extends ServiceContainer {
68404
68609
  i18n,
68405
68610
  params,
68406
68611
  pathname,
68407
- prefetchedData,
68612
+ serverQueries,
68613
+ userAgent,
68408
68614
  url,
68409
68615
  user,
68410
68616
  viewData,
@@ -68423,7 +68629,7 @@ class ViewRouterServiceContainer extends ServiceContainer {
68423
68629
  [pageDataKey]: viewData
68424
68630
  },
68425
68631
  __csrf: csrfTokenHMAC.toString("base64"),
68426
- prefetchedData,
68632
+ prefetchedData: {},
68427
68633
  i18n,
68428
68634
  auth: { user },
68429
68635
  routeManifest: this.clientRouteManifest,
@@ -68444,7 +68650,15 @@ class ViewRouterServiceContainer extends ServiceContainer {
68444
68650
  const Root = this.root;
68445
68651
  const currentViews = this.routeManifest[currentPathName];
68446
68652
  return async (params2) => {
68447
- const { bootstrapModules, loaders, getStyles, viewImportMap, cssManifest, ogMap } = params2;
68653
+ const {
68654
+ bootstrapModules,
68655
+ loaders,
68656
+ getStyles,
68657
+ viewImportMap,
68658
+ cssManifest,
68659
+ ogMap,
68660
+ viewModules
68661
+ } = params2;
68448
68662
  if (isOgRequest) {
68449
68663
  let ogHandler = null;
68450
68664
  let ogComponent = null;
@@ -68483,6 +68697,11 @@ class ViewRouterServiceContainer extends ServiceContainer {
68483
68697
  }
68484
68698
  result.data["cssManifest"] = cssManifest;
68485
68699
  const styles = await getStyles(currentViews);
68700
+ result.data.prefetchedData = serverQueries.snapshotResolved();
68701
+ serverQueries.markRenderStart();
68702
+ const deadline = new AbortController;
68703
+ const deadlineTimer = setTimeout(() => deadline.abort(), STREAM_DEADLINE_MS);
68704
+ const isBot = isBotUserAgent(userAgent);
68486
68705
  try {
68487
68706
  const stream = await renderToReadableStream(import_react.createElement(import_react.Fragment, {
68488
68707
  children: [
@@ -68496,18 +68715,32 @@ class ViewRouterServiceContainer extends ServiceContainer {
68496
68715
  import_react.createElement(Root, {
68497
68716
  data: result.data,
68498
68717
  viewImportMap,
68718
+ viewModules,
68719
+ serverQueries,
68499
68720
  key: "root"
68500
68721
  })
68501
68722
  ]
68502
68723
  }), {
68503
68724
  bootstrapScriptContent: `window.__GEMI_DATA__ = ${JSON.stringify(result.data)}; window.loaders=${loaders}`,
68504
- bootstrapModules
68725
+ bootstrapModules,
68726
+ signal: deadline.signal,
68727
+ ...isBot ? { progressiveChunkSize: Number.MAX_SAFE_INTEGER } : {},
68728
+ onError(error) {
68729
+ if (true) {
68730
+ console.error(error);
68731
+ }
68732
+ }
68505
68733
  });
68506
- return new Response(stream, {
68734
+ stream.allReady.catch(() => {}).finally(() => clearTimeout(deadlineTimer));
68735
+ if (isBot) {
68736
+ await stream.allReady.catch(() => {});
68737
+ }
68738
+ return new Response(injectQueryPayloads(stream, serverQueries), {
68507
68739
  status: !currentPathName ? 404 : 200,
68508
68740
  headers
68509
68741
  });
68510
68742
  } catch (err2) {
68743
+ clearTimeout(deadlineTimer);
68511
68744
  const stream = await renderToReadableStream(import_react.createElement("div"), {
68512
68745
  bootstrapScriptContent: `window.error= ${JSON.stringify(err2.message)}; window.stack_trace=${JSON.stringify(err2.stack)};window.__GEMI_DATA__ = ${JSON.stringify(result.data)}; window.loaders=${loaders}`,
68513
68746
  bootstrapModules: ["/render-error.js", ...bootstrapModules]
@@ -68584,6 +68817,7 @@ class ViewRouterServiceContainer extends ServiceContainer {
68584
68817
  return await RequestContext.run(httpRequest, async () => {
68585
68818
  let pageData = null;
68586
68819
  const ctx = RequestContext.getStore();
68820
+ ctx.serverQueries = new ServerQueryStore(createServerQueryFetcher(req));
68587
68821
  if (urlLocale) {
68588
68822
  const locale = urlLocale.replaceAll("/", "");
68589
68823
  I18n.setLocale(locale);
@@ -68615,21 +68849,18 @@ class ViewRouterServiceContainer extends ServiceContainer {
68615
68849
  defaultLocale: i18nServiceContainer2.service.defaultLocale
68616
68850
  };
68617
68851
  }
68618
- const handlerPromises = handlers.map((fn2) => fn2(httpRequest2));
68619
- const prefetchPromises = Array.from(ctx.prefetchPromiseQueue).map((fn2) => fn2());
68620
- const [data] = await Promise.all([
68621
- Promise.all(handlerPromises),
68622
- Promise.all(prefetchPromises)
68623
- ]);
68852
+ const data = await Promise.all(handlers.map((fn2) => fn2(httpRequest2)));
68853
+ if (isViewDataRequest) {
68854
+ await ctx.serverQueries.allSettled();
68855
+ }
68624
68856
  const cookies = ctx.cookies;
68625
68857
  const headers = ctx.headers;
68626
- const prefetchedResources = ctx.prefetchedResources;
68627
68858
  pageData = {
68628
68859
  data,
68629
68860
  cookies,
68630
68861
  headers,
68631
68862
  user: ctx.user,
68632
- prefetchedData: Object.fromEntries(prefetchedResources.entries()),
68863
+ prefetchedData: ctx.serverQueries.snapshotResolved(),
68633
68864
  currentPathName: httpRequest2.routePath,
68634
68865
  params: httpRequest2.params,
68635
68866
  urlLocaleSegment,
@@ -68701,7 +68932,8 @@ class ViewRouterServiceContainer extends ServiceContainer {
68701
68932
  i18n,
68702
68933
  params: params2,
68703
68934
  pathname: url.pathname,
68704
- prefetchedData: pageData.prefetchedData,
68935
+ serverQueries: ctx.serverQueries,
68936
+ userAgent: req.headers.get("user-agent"),
68705
68937
  url,
68706
68938
  user,
68707
68939
  viewData,
@@ -69749,5 +69981,5 @@ program.command("app:route-manifest").action(async () => {
69749
69981
  });
69750
69982
  program.parse();
69751
69983
 
69752
- //# debugId=971DE98EAA769ECD64756E2164756E21
69984
+ //# debugId=98CFC1ED06DD2FFA64756E2164756E21
69753
69985
  //# sourceMappingURL=gemi.js.map