gemi 0.48.1 → 0.49.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/app/index.js +1 -1
  2. package/dist/bin/gemi.js +249 -19
  3. package/dist/bin/gemi.js.map +10 -5
  4. package/dist/chunk-8jmxfq66.js +5 -0
  5. package/dist/{chunk-6ktnnryj.js.map → chunk-8jmxfq66.js.map} +3 -3
  6. package/dist/chunk-90hx7kw2.js +6 -0
  7. package/dist/{chunk-28sma4j1.js.map → chunk-90hx7kw2.js.map} +2 -2
  8. package/dist/{chunk-r64ba1pf.js → chunk-b2xv683r.js} +1 -1
  9. package/dist/chunk-vct7btyq.js +55 -0
  10. package/dist/{chunk-72nhqkwa.js.map → chunk-vct7btyq.js.map} +3 -3
  11. package/dist/{chunk-d9k245jt.js → chunk-w6r5jtgb.js} +18 -18
  12. package/dist/{chunk-d9k245jt.js.map → chunk-w6r5jtgb.js.map} +9 -5
  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 +10 -0
  16. package/dist/client/ClientRouter.d.ts.map +1 -1
  17. package/dist/client/ClientRouterContext.d.ts.map +1 -1
  18. package/dist/client/ComponentContext.d.ts +10 -0
  19. package/dist/client/ComponentContext.d.ts.map +1 -1
  20. package/dist/client/QueryError.d.ts +13 -0
  21. package/dist/client/QueryError.d.ts.map +1 -0
  22. package/dist/client/QueryManagerContext.d.ts +1 -0
  23. package/dist/client/QueryManagerContext.d.ts.map +1 -1
  24. package/dist/client/QueryResource.d.ts +36 -0
  25. package/dist/client/QueryResource.d.ts.map +1 -1
  26. package/dist/client/ServerQueryContext.d.ts +25 -0
  27. package/dist/client/ServerQueryContext.d.ts.map +1 -0
  28. package/dist/client/auth/useSignIn.d.ts.map +1 -1
  29. package/dist/client/auth/useUser.d.ts.map +1 -1
  30. package/dist/client/createRoot.d.ts.map +1 -1
  31. package/dist/client/index.d.ts +1 -0
  32. package/dist/client/index.d.ts.map +1 -1
  33. package/dist/client/index.js +430 -216
  34. package/dist/client/index.js.map +1 -1
  35. package/dist/client/useQuery.d.ts +26 -9
  36. package/dist/client/useQuery.d.ts.map +1 -1
  37. package/dist/facades/Prefetch.d.ts +18 -13
  38. package/dist/facades/Prefetch.d.ts.map +1 -1
  39. package/dist/facades/index.js +1 -1
  40. package/dist/http/HttpRequest.d.ts +1 -2
  41. package/dist/http/HttpRequest.d.ts.map +1 -1
  42. package/dist/http/index.js +1 -1
  43. package/dist/http/requestContext.d.ts +7 -2
  44. package/dist/http/requestContext.d.ts.map +1 -1
  45. package/dist/i18n/index.js +1 -1
  46. package/dist/kernel/index.js +1 -1
  47. package/dist/server/httpDev.d.ts.map +1 -1
  48. package/dist/server/httpProd.d.ts.map +1 -1
  49. package/dist/server/index.js +1 -1
  50. package/dist/services/router/ServerQueryStore.d.ts +80 -0
  51. package/dist/services/router/ServerQueryStore.d.ts.map +1 -0
  52. package/dist/services/router/ViewRouterServiceContainer.d.ts +6 -0
  53. package/dist/services/router/ViewRouterServiceContainer.d.ts.map +1 -1
  54. package/dist/services/router/serverQueryFetcher.d.ts +12 -0
  55. package/dist/services/router/serverQueryFetcher.d.ts.map +1 -0
  56. package/dist/services/router/streamQueryInjection.d.ts +36 -0
  57. package/dist/services/router/streamQueryInjection.d.ts.map +1 -0
  58. package/package.json +1 -1
  59. package/dist/chunk-28sma4j1.js +0 -6
  60. package/dist/chunk-6ktnnryj.js +0 -5
  61. package/dist/chunk-72nhqkwa.js +0 -55
  62. package/dist/chunk-evkv0xa7.js +0 -4
  63. /package/dist/{chunk-r64ba1pf.js.map → chunk-b2xv683r.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-b2xv683r.js";import"../chunk-w6r5jtgb.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,10 @@ 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);
68486
68704
  try {
68487
68705
  const stream = await renderToReadableStream(import_react.createElement(import_react.Fragment, {
68488
68706
  children: [
@@ -68496,18 +68714,31 @@ class ViewRouterServiceContainer extends ServiceContainer {
68496
68714
  import_react.createElement(Root, {
68497
68715
  data: result.data,
68498
68716
  viewImportMap,
68717
+ viewModules,
68718
+ serverQueries,
68499
68719
  key: "root"
68500
68720
  })
68501
68721
  ]
68502
68722
  }), {
68503
68723
  bootstrapScriptContent: `window.__GEMI_DATA__ = ${JSON.stringify(result.data)}; window.loaders=${loaders}`,
68504
- bootstrapModules
68724
+ bootstrapModules,
68725
+ signal: deadline.signal,
68726
+ onError(error) {
68727
+ if (true) {
68728
+ console.error(error);
68729
+ }
68730
+ }
68505
68731
  });
68506
- return new Response(stream, {
68732
+ stream.allReady.catch(() => {}).finally(() => clearTimeout(deadlineTimer));
68733
+ if (isBotUserAgent(userAgent)) {
68734
+ await stream.allReady.catch(() => {});
68735
+ }
68736
+ return new Response(injectQueryPayloads(stream, serverQueries), {
68507
68737
  status: !currentPathName ? 404 : 200,
68508
68738
  headers
68509
68739
  });
68510
68740
  } catch (err2) {
68741
+ clearTimeout(deadlineTimer);
68511
68742
  const stream = await renderToReadableStream(import_react.createElement("div"), {
68512
68743
  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
68744
  bootstrapModules: ["/render-error.js", ...bootstrapModules]
@@ -68584,6 +68815,7 @@ class ViewRouterServiceContainer extends ServiceContainer {
68584
68815
  return await RequestContext.run(httpRequest, async () => {
68585
68816
  let pageData = null;
68586
68817
  const ctx = RequestContext.getStore();
68818
+ ctx.serverQueries = new ServerQueryStore(createServerQueryFetcher(req));
68587
68819
  if (urlLocale) {
68588
68820
  const locale = urlLocale.replaceAll("/", "");
68589
68821
  I18n.setLocale(locale);
@@ -68615,21 +68847,18 @@ class ViewRouterServiceContainer extends ServiceContainer {
68615
68847
  defaultLocale: i18nServiceContainer2.service.defaultLocale
68616
68848
  };
68617
68849
  }
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
- ]);
68850
+ const data = await Promise.all(handlers.map((fn2) => fn2(httpRequest2)));
68851
+ if (isViewDataRequest) {
68852
+ await ctx.serverQueries.allSettled();
68853
+ }
68624
68854
  const cookies = ctx.cookies;
68625
68855
  const headers = ctx.headers;
68626
- const prefetchedResources = ctx.prefetchedResources;
68627
68856
  pageData = {
68628
68857
  data,
68629
68858
  cookies,
68630
68859
  headers,
68631
68860
  user: ctx.user,
68632
- prefetchedData: Object.fromEntries(prefetchedResources.entries()),
68861
+ prefetchedData: ctx.serverQueries.snapshotResolved(),
68633
68862
  currentPathName: httpRequest2.routePath,
68634
68863
  params: httpRequest2.params,
68635
68864
  urlLocaleSegment,
@@ -68701,7 +68930,8 @@ class ViewRouterServiceContainer extends ServiceContainer {
68701
68930
  i18n,
68702
68931
  params: params2,
68703
68932
  pathname: url.pathname,
68704
- prefetchedData: pageData.prefetchedData,
68933
+ serverQueries: ctx.serverQueries,
68934
+ userAgent: req.headers.get("user-agent"),
68705
68935
  url,
68706
68936
  user,
68707
68937
  viewData,
@@ -69749,5 +69979,5 @@ program.command("app:route-manifest").action(async () => {
69749
69979
  });
69750
69980
  program.parse();
69751
69981
 
69752
- //# debugId=971DE98EAA769ECD64756E2164756E21
69982
+ //# debugId=8FF3EC642A31F68D64756E2164756E21
69753
69983
  //# sourceMappingURL=gemi.js.map