@getcronit/pylon 2.10.0-canary-20250206024306.0b965bfde9f8e1db5b2728aeb8259def3c857325 → 3.0.0-canary-20250211153808.b2b63f4e67c55542413f7be6d62ce8139cfcbdbe

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/dist/index.js CHANGED
@@ -148,13 +148,13 @@ var resolversToGraphQLResolvers = (resolvers, configureContext) => {
148
148
  );
149
149
  }
150
150
  ctx2?.set("graphqlResolveInfo", info);
151
- const auth2 = ctx2?.get("auth");
152
- if (auth2?.active) {
151
+ const auth = ctx2?.get("auth");
152
+ if (auth?.user) {
153
153
  scope.setUser({
154
- id: auth2.sub,
155
- username: auth2.preferred_username,
156
- email: auth2.email,
157
- details: auth2
154
+ id: auth.user.sub,
155
+ username: auth.user.preferred_username,
156
+ email: auth.user.email,
157
+ details: auth.user
158
158
  });
159
159
  }
160
160
  let type = null;
@@ -273,18 +273,246 @@ var ServiceError = class extends GraphQLError {
273
273
  }
274
274
  };
275
275
 
276
- // src/auth/index.ts
277
- import jwt from "jsonwebtoken";
276
+ // src/plugins/use-auth/use-auth.ts
277
+ import { promises as fs } from "fs";
278
+ import { deleteCookie, getCookie, setCookie } from "hono/cookie";
279
+ import { HTTPException } from "hono/http-exception";
280
+ import * as openid from "openid-client";
278
281
  import path from "path";
279
- import { HTTPException as HTTPException2 } from "hono/http-exception";
280
- import { env as env2 } from "hono/adapter";
281
- import * as Sentry2 from "@sentry/bun";
282
- import { existsSync, readFileSync } from "fs";
283
- import { sendFunctionEvent as sendFunctionEvent4 } from "@getcronit/pylon-telemetry";
284
282
 
285
- // src/auth/decorators/requireAuth.ts
286
- import { sendFunctionEvent as sendFunctionEvent3 } from "@getcronit/pylon-telemetry";
287
- import { HTTPException } from "hono/http-exception";
283
+ // src/plugins/use-auth/import-private-key.ts
284
+ import * as crypto2 from "crypto";
285
+ function str2ab(str) {
286
+ const buf = new ArrayBuffer(str.length);
287
+ const bufView = new Uint8Array(buf);
288
+ for (let i = 0, strLen = str.length; i < strLen; i++) {
289
+ bufView[i] = str.charCodeAt(i);
290
+ }
291
+ return buf;
292
+ }
293
+ var convertPKCS1ToPKCS8 = (pkcs1) => {
294
+ const key = crypto2.createPrivateKey(pkcs1);
295
+ return key.export({
296
+ type: "pkcs8",
297
+ format: "pem"
298
+ });
299
+ };
300
+ function importPKCS8PrivateKey(pem) {
301
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
302
+ const pemFooter = "-----END PRIVATE KEY-----";
303
+ const pemContents = pem.substring(
304
+ pemHeader.length,
305
+ pem.length - pemFooter.length - 1
306
+ );
307
+ const binaryDerString = atob(pemContents);
308
+ const binaryDer = str2ab(binaryDerString);
309
+ return crypto2.subtle.importKey(
310
+ "pkcs8",
311
+ binaryDer,
312
+ {
313
+ name: "RSASSA-PKCS1-v1_5",
314
+ hash: "SHA-256"
315
+ },
316
+ true,
317
+ ["sign"]
318
+ );
319
+ }
320
+ var importPrivateKey = async (pkcs1Pem) => {
321
+ const pkcs8Pem = convertPKCS1ToPKCS8(pkcs1Pem);
322
+ return await importPKCS8PrivateKey(pkcs8Pem);
323
+ };
324
+
325
+ // src/plugins/use-auth/use-auth.ts
326
+ var loadAuthKey = async (keyPath) => {
327
+ const authKeyFilePath = path.join(process.cwd(), keyPath);
328
+ const env3 = getContext().env;
329
+ if (env3.AUTH_KEY) {
330
+ try {
331
+ return JSON.parse(env3.AUTH_KEY);
332
+ } catch (error) {
333
+ throw new Error(
334
+ "Error while reading AUTH_KEY. Make sure it is valid JSON"
335
+ );
336
+ }
337
+ }
338
+ try {
339
+ const ketFileContent = await fs.readFile(authKeyFilePath, "utf-8");
340
+ try {
341
+ return JSON.parse(ketFileContent);
342
+ } catch (error) {
343
+ throw new Error(
344
+ "Error while reading key file. Make sure it is valid JSON"
345
+ );
346
+ }
347
+ } catch (error) {
348
+ throw new Error("Error while reading key file. Make sure it exists");
349
+ }
350
+ };
351
+ var openidConfigCache;
352
+ var bootstrapAuth = async (issuer, keyPath) => {
353
+ if (!openidConfigCache) {
354
+ const authKey = await loadAuthKey(keyPath);
355
+ openidConfigCache = await openid.discovery(
356
+ new URL(issuer),
357
+ authKey.clientId,
358
+ void 0,
359
+ openid.PrivateKeyJwt({
360
+ key: await importPrivateKey(authKey.key),
361
+ kid: authKey.keyId
362
+ })
363
+ );
364
+ }
365
+ return openidConfigCache;
366
+ };
367
+ var PylonAuthException = class extends HTTPException {
368
+ // Same constructor as HTTPException
369
+ constructor(...args) {
370
+ args[1] = {
371
+ ...args[1],
372
+ message: `PylonAuthException: ${args[1]?.message}`
373
+ };
374
+ super(...args);
375
+ }
376
+ };
377
+ function useAuth(args) {
378
+ const { issuer, endpoint = "/auth", keyPath = "key.json" } = args;
379
+ const loginPath = `${endpoint}/login`;
380
+ const logoutPath = `${endpoint}/logout`;
381
+ const callbackPath = `${endpoint}/callback`;
382
+ return {
383
+ middleware: async (ctx, next) => {
384
+ const openidConfig = await bootstrapAuth(issuer, keyPath);
385
+ ctx.set("auth", { openidConfig });
386
+ const authCookieToken = getCookie(ctx, "pylon-auth");
387
+ const authHeader = ctx.req.header("Authorization");
388
+ const authQueryToken = ctx.req.query("token");
389
+ if (authCookieToken || authHeader || authQueryToken) {
390
+ let token;
391
+ if (authHeader) {
392
+ const [type, value] = authHeader.split(" ");
393
+ if (type === "Bearer") {
394
+ token = value;
395
+ }
396
+ } else if (authQueryToken) {
397
+ token = authQueryToken;
398
+ } else if (authCookieToken) {
399
+ token = authCookieToken;
400
+ }
401
+ if (!token) {
402
+ throw new PylonAuthException(401, {
403
+ message: "Invalid token"
404
+ });
405
+ }
406
+ const introspection = await openid.tokenIntrospection(
407
+ openidConfig,
408
+ token,
409
+ {
410
+ scope: "openid email profile"
411
+ }
412
+ );
413
+ if (!introspection.active) {
414
+ throw new PylonAuthException(401, {
415
+ message: "Token is not active"
416
+ });
417
+ }
418
+ if (!introspection.sub) {
419
+ throw new PylonAuthException(401, {
420
+ message: "Token is missing subject"
421
+ });
422
+ }
423
+ const userInfo = await openid.fetchUserInfo(
424
+ openidConfig,
425
+ token,
426
+ introspection.sub
427
+ );
428
+ const roles = Object.keys(
429
+ introspection["urn:zitadel:iam:org:projects:roles"]?.valueOf() || {}
430
+ );
431
+ ctx.set("auth", {
432
+ user: {
433
+ ...userInfo,
434
+ roles
435
+ },
436
+ openidConfig
437
+ });
438
+ return next();
439
+ }
440
+ },
441
+ setup(app2) {
442
+ app2.get(loginPath, async (ctx) => {
443
+ const openidConfig = ctx.get("auth").openidConfig;
444
+ const codeVerifier = openid.randomPKCECodeVerifier();
445
+ const codeChallenge = await openid.calculatePKCECodeChallenge(
446
+ codeVerifier
447
+ );
448
+ setCookie(ctx, "pylon_code_verifier", codeVerifier, {
449
+ httpOnly: true,
450
+ maxAge: 300
451
+ // 5 minutes
452
+ });
453
+ let scope = "openid profile email urn:zitadel:iam:user:resourceowner urn:zitadel:iam:org:projects:roles";
454
+ const parameters = {
455
+ scope,
456
+ code_challenge: codeChallenge,
457
+ code_challenge_method: "S256",
458
+ redirect_uri: new URL(ctx.req.url).origin + "/auth/callback",
459
+ state: openid.randomState()
460
+ };
461
+ const authorizationUrl = openid.buildAuthorizationUrl(
462
+ openidConfig,
463
+ parameters
464
+ );
465
+ return ctx.redirect(authorizationUrl);
466
+ });
467
+ app2.get(logoutPath, async (ctx) => {
468
+ deleteCookie(ctx, "pylon-auth");
469
+ return ctx.redirect("/");
470
+ });
471
+ app2.get(callbackPath, async (ctx) => {
472
+ const openidConfig = ctx.get("auth").openidConfig;
473
+ const params = ctx.req.query();
474
+ const code = params.code;
475
+ const state = params.state;
476
+ if (!code || !state) {
477
+ throw new PylonAuthException(400, {
478
+ message: "Missing authorization code or state"
479
+ });
480
+ }
481
+ const codeVerifier = getCookie(ctx, "pylon_code_verifier");
482
+ if (!codeVerifier) {
483
+ throw new PylonAuthException(400, {
484
+ message: "Missing code verifier"
485
+ });
486
+ }
487
+ try {
488
+ const cbUrl = new URL(ctx.req.url);
489
+ let tokenSet = await openid.authorizationCodeGrant(
490
+ openidConfig,
491
+ cbUrl,
492
+ {
493
+ pkceCodeVerifier: codeVerifier,
494
+ expectedState: state
495
+ },
496
+ cbUrl.searchParams
497
+ );
498
+ setCookie(ctx, `pylon-auth`, tokenSet.access_token, {
499
+ httpOnly: true,
500
+ maxAge: tokenSet.expires_in || 3600
501
+ // Default to 1 hour if not specified
502
+ });
503
+ return ctx.redirect("/");
504
+ } catch (error) {
505
+ console.error("Error during token exchange:", error);
506
+ return ctx.text("Authentication failed!", 500);
507
+ }
508
+ });
509
+ }
510
+ };
511
+ }
512
+
513
+ // src/plugins/use-auth/auth-require.ts
514
+ import { env as env2 } from "hono/adapter";
515
+ import { HTTPException as HTTPException2 } from "hono/http-exception";
288
516
 
289
517
  // src/create-decorator.ts
290
518
  import { sendFunctionEvent as sendFunctionEvent2 } from "@getcronit/pylon-telemetry";
@@ -335,20 +563,47 @@ function createDecorator(callback) {
335
563
  return MyDecorator;
336
564
  }
337
565
 
338
- // src/auth/decorators/requireAuth.ts
566
+ // src/plugins/use-auth/auth-require.ts
567
+ var authMiddleware = (checks = {}) => {
568
+ const middleware = async (ctx, next) => {
569
+ const AUTH_PROJECT_ID = env2(ctx).AUTH_PROJECT_ID;
570
+ const auth = ctx.get("auth");
571
+ if (!auth) {
572
+ throw new HTTPException2(401, {
573
+ message: "Authentication required"
574
+ });
575
+ }
576
+ if (checks.roles && auth.user) {
577
+ const roles = auth.user.roles;
578
+ const hasRole = checks.roles.some((role) => {
579
+ return roles.includes(role) || roles.includes(`${AUTH_PROJECT_ID}:${role}`);
580
+ });
581
+ if (!hasRole) {
582
+ const resError = new Response("Forbidden", {
583
+ status: 403,
584
+ statusText: "Forbidden",
585
+ headers: {
586
+ "Missing-Roles": checks.roles.join(","),
587
+ "Obtained-Roles": roles.join(",")
588
+ }
589
+ });
590
+ throw new HTTPException2(resError.status, {
591
+ res: resError
592
+ });
593
+ }
594
+ }
595
+ return next();
596
+ };
597
+ return middleware;
598
+ };
339
599
  function requireAuth(checks) {
340
- sendFunctionEvent3({
341
- name: "requireAuth",
342
- duration: 0
343
- }).then(() => {
344
- });
345
600
  const checkAuth = async (c) => {
346
601
  const ctx = await c;
347
602
  try {
348
- await auth.require(checks)(ctx, async () => {
603
+ await authMiddleware(checks)(ctx, async () => {
349
604
  });
350
605
  } catch (e) {
351
- if (e instanceof HTTPException) {
606
+ if (e instanceof HTTPException2) {
352
607
  if (e.status === 401) {
353
608
  throw new ServiceError(e.message, {
354
609
  statusCode: 401,
@@ -377,214 +632,11 @@ function requireAuth(checks) {
377
632
  });
378
633
  }
379
634
 
380
- // src/auth/index.ts
381
- var authInitialize = () => {
382
- const authKeyFilePath = path.join(process.cwd(), "key.json");
383
- let API_PRIVATE_KEY_FILE = void 0;
384
- if (existsSync(authKeyFilePath)) {
385
- try {
386
- API_PRIVATE_KEY_FILE = JSON.parse(readFileSync(authKeyFilePath, "utf-8"));
387
- } catch (error) {
388
- throw new Error(
389
- "Error while reading key file. Make sure it is valid JSON"
390
- );
391
- }
392
- }
393
- const middleware = Sentry2.startSpan(
394
- {
395
- name: "AuthMiddleware",
396
- op: "auth"
397
- },
398
- () => async function(ctx, next) {
399
- const AUTH_ISSUER = env2(ctx).AUTH_ISSUER;
400
- if (!AUTH_ISSUER) {
401
- throw new Error("AUTH_ISSUER is not set");
402
- }
403
- if (!API_PRIVATE_KEY_FILE) {
404
- const AUTH_KEY = env2(ctx).AUTH_KEY;
405
- API_PRIVATE_KEY_FILE = AUTH_KEY ? JSON.parse(AUTH_KEY) : void 0;
406
- }
407
- if (!API_PRIVATE_KEY_FILE) {
408
- throw new Error(
409
- "You have initialized the auth middleware without a private key file"
410
- );
411
- }
412
- const AUTH_PROJECT_ID = env2(ctx).AUTH_PROJECT_ID;
413
- const ZITADEL_INTROSPECTION_URL = `${AUTH_ISSUER}/oauth/v2/introspect`;
414
- async function getRolesFromToken(tokenString) {
415
- const response = await fetch(
416
- `${AUTH_ISSUER}/auth/v1/usergrants/me/_search`,
417
- {
418
- method: "POST",
419
- headers: {
420
- "Content-Type": "application/json",
421
- Authorization: `Bearer ${tokenString}`
422
- }
423
- }
424
- );
425
- const data = await response.json();
426
- const userRoles = data.result?.map((grant) => {
427
- return (grant.roles || []).map((role) => {
428
- return `${grant.projectId}:${role}`;
429
- });
430
- }) || [];
431
- const projectScopedRoles = userRoles.flat();
432
- const rolesSet = new Set(projectScopedRoles);
433
- if (AUTH_PROJECT_ID) {
434
- for (const role of projectScopedRoles) {
435
- const [projectId, ...roleNameParts] = role.split(":");
436
- const roleName = roleNameParts.join(":");
437
- if (projectId === AUTH_PROJECT_ID) {
438
- rolesSet.add(roleName);
439
- }
440
- }
441
- }
442
- return Array.from(rolesSet);
443
- }
444
- async function introspectToken(tokenString) {
445
- if (!API_PRIVATE_KEY_FILE) {
446
- throw new Error("Internal error: API_PRIVATE_KEY_FILE is not set");
447
- }
448
- const payload = {
449
- iss: API_PRIVATE_KEY_FILE.clientId,
450
- sub: API_PRIVATE_KEY_FILE.clientId,
451
- aud: AUTH_ISSUER,
452
- exp: Math.floor(Date.now() / 1e3) + 60 * 60,
453
- // Expires in 1 hour
454
- iat: Math.floor(Date.now() / 1e3)
455
- };
456
- const headers = {
457
- alg: "RS256",
458
- kid: API_PRIVATE_KEY_FILE.keyId
459
- };
460
- const jwtToken = jwt.sign(payload, API_PRIVATE_KEY_FILE.key, {
461
- algorithm: "RS256",
462
- header: headers
463
- });
464
- const scopeSet = /* @__PURE__ */ new Set();
465
- scopeSet.add("openid");
466
- scopeSet.add("profile");
467
- scopeSet.add("email");
468
- if (AUTH_PROJECT_ID) {
469
- scopeSet.add(
470
- `urn:zitadel:iam:org:project:id:${AUTH_PROJECT_ID}:aud`
471
- );
472
- }
473
- const scope = Array.from(scopeSet).join(" ");
474
- const body = new URLSearchParams({
475
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
476
- client_assertion: jwtToken,
477
- token: tokenString,
478
- scope
479
- }).toString();
480
- try {
481
- const response = await fetch(ZITADEL_INTROSPECTION_URL, {
482
- method: "POST",
483
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
484
- body
485
- });
486
- if (!response.ok) {
487
- throw new Error("Network response was not ok");
488
- }
489
- const tokenData = await response.json();
490
- const roles = await getRolesFromToken(tokenString);
491
- const state = {
492
- ...tokenData,
493
- roles
494
- };
495
- return state;
496
- } catch (error) {
497
- console.error("Error while introspecting token", error);
498
- throw new Error("Token introspection failed");
499
- }
500
- }
501
- let token = void 0;
502
- if (ctx.req.header("Authorization")) {
503
- const authHeader = ctx.req.header("Authorization");
504
- if (authHeader) {
505
- const parts = authHeader.split(" ");
506
- if (parts.length === 2 && parts[0] === "Bearer") {
507
- token = parts[1];
508
- }
509
- }
510
- }
511
- if (!token) {
512
- const queryToken = ctx.req.query("token");
513
- if (queryToken) {
514
- token = queryToken;
515
- }
516
- }
517
- if (token) {
518
- const auth2 = await introspectToken(token);
519
- if (auth2.active) {
520
- ctx.set("auth", auth2);
521
- Sentry2.setUser({
522
- id: auth2.sub,
523
- username: auth2.preferred_username,
524
- email: auth2.email,
525
- details: auth2
526
- });
527
- }
528
- }
529
- return next();
530
- }
531
- );
532
- sendFunctionEvent4({
533
- name: "authInitialize",
534
- duration: 0
535
- }).then(() => {
536
- });
537
- return middleware;
538
- };
539
- var authRequire = (checks = {}) => {
540
- sendFunctionEvent4({
541
- name: "authRequire",
542
- duration: 0
543
- }).then(() => {
544
- });
545
- const middleware = async (ctx, next) => {
546
- const AUTH_PROJECT_ID = env2(ctx).AUTH_PROJECT_ID;
547
- const auth2 = ctx.get("auth");
548
- if (!auth2) {
549
- throw new HTTPException2(401, {
550
- message: "Authentication required"
551
- });
552
- }
553
- if (checks.roles) {
554
- const roles = auth2.roles;
555
- const hasRole = checks.roles.some((role) => {
556
- return roles.includes(role) || roles.includes(`${AUTH_PROJECT_ID}:${role}`);
557
- });
558
- if (!hasRole) {
559
- const resError = new Response("Forbidden", {
560
- status: 403,
561
- statusText: "Forbidden",
562
- headers: {
563
- "Missing-Roles": checks.roles.join(","),
564
- "Obtained-Roles": roles.join(",")
565
- }
566
- });
567
- throw new HTTPException2(resError.status, { res: resError });
568
- }
569
- }
570
- return next();
571
- };
572
- sendFunctionEvent4({
573
- name: "authRequire",
574
- duration: 0
575
- }).then(() => {
576
- });
577
- return middleware;
578
- };
579
- var auth = {
580
- initialize: authInitialize,
581
- require: authRequire
582
- };
583
-
584
635
  // src/app/index.ts
585
636
  import { Hono } from "hono";
586
637
  import { logger } from "hono/logger";
587
638
  import { sentry } from "@hono/sentry";
639
+ import { except } from "hono/combine";
588
640
  var app = new Hono();
589
641
  app.use("*", sentry());
590
642
  app.use("*", async (c, next) => {
@@ -598,7 +650,7 @@ app.use("*", async (c, next) => {
598
650
  });
599
651
  });
600
652
  });
601
- app.use("*", logger());
653
+ app.use("*", except(["/__pylon/static/*"], logger()));
602
654
  app.use((c, next) => {
603
655
  c.req.id = crypto.randomUUID();
604
656
  return next();
@@ -633,7 +685,7 @@ import {
633
685
  handleStreamOrSingleExecutionResult,
634
686
  isOriginalGraphQLError
635
687
  } from "@envelop/core";
636
- import * as Sentry3 from "@sentry/node";
688
+ import * as Sentry2 from "@sentry/node";
637
689
  var defaultSkipError = isOriginalGraphQLError;
638
690
  var useSentry = (options = {}) => {
639
691
  function pick(key, defaultValue) {
@@ -673,7 +725,7 @@ var useSentry = (options = {}) => {
673
725
  ...addedTags
674
726
  };
675
727
  if (options.configureScope) {
676
- options.configureScope(args, Sentry3.getCurrentScope());
728
+ options.configureScope(args, Sentry2.getCurrentScope());
677
729
  }
678
730
  return {
679
731
  onExecuteDone(payload) {
@@ -681,7 +733,7 @@ var useSentry = (options = {}) => {
681
733
  result,
682
734
  setResult
683
735
  }) => {
684
- Sentry3.startSpanManual(
736
+ Sentry2.startSpanManual(
685
737
  {
686
738
  op,
687
739
  name: opName,
@@ -696,7 +748,7 @@ var useSentry = (options = {}) => {
696
748
  span.setAttribute("result", JSON.stringify(result));
697
749
  }
698
750
  if (result.errors && result.errors.length > 0) {
699
- Sentry3.withScope((scope) => {
751
+ Sentry2.withScope((scope) => {
700
752
  scope.setTransactionName(opName);
701
753
  scope.setTag("operation", operationType);
702
754
  scope.setTag("operationName", opName);
@@ -722,7 +774,7 @@ var useSentry = (options = {}) => {
722
774
  level: "debug"
723
775
  });
724
776
  }
725
- const eventId = Sentry3.captureException(
777
+ const eventId = Sentry2.captureException(
726
778
  err.originalError,
727
779
  {
728
780
  fingerprint: [
@@ -760,7 +812,7 @@ var useSentry = (options = {}) => {
760
812
  };
761
813
 
762
814
  // src/app/pylon-handler.ts
763
- import { readFileSync as readFileSync2 } from "fs";
815
+ import { readFileSync } from "fs";
764
816
  import path2 from "path";
765
817
 
766
818
  // src/plugins/use-viewer.ts
@@ -1055,7 +1107,7 @@ var handler = (options) => {
1055
1107
  if (!typeDefs) {
1056
1108
  const schemaPath = path2.join(process.cwd(), ".pylon", "schema.graphql");
1057
1109
  if (schemaPath) {
1058
- typeDefs = readFileSync2(schemaPath, "utf-8");
1110
+ typeDefs = readFileSync(schemaPath, "utf-8");
1059
1111
  }
1060
1112
  }
1061
1113
  if (!typeDefs) {
@@ -1134,18 +1186,20 @@ var handler = (options) => {
1134
1186
  };
1135
1187
 
1136
1188
  // src/get-env.ts
1137
- import { sendFunctionEvent as sendFunctionEvent5 } from "@getcronit/pylon-telemetry";
1189
+ import { sendFunctionEvent as sendFunctionEvent3 } from "@getcronit/pylon-telemetry";
1138
1190
  function getEnv() {
1139
1191
  const start = Date.now();
1140
1192
  const skipTracing = arguments[0] === true;
1141
1193
  try {
1142
1194
  const context = asyncContext.getStore();
1143
- return context.env || process.env || {};
1195
+ const ctx = context.env || process.env || {};
1196
+ ctx.NODE_ENV = ctx.NODE_ENV || process.env.NODE_ENV || "development";
1197
+ return ctx;
1144
1198
  } catch {
1145
1199
  return process.env;
1146
1200
  } finally {
1147
1201
  if (!skipTracing) {
1148
- sendFunctionEvent5({
1202
+ sendFunctionEvent3({
1149
1203
  name: "getEnv",
1150
1204
  duration: Date.now() - start
1151
1205
  }).then(() => {
@@ -1156,17 +1210,660 @@ function getEnv() {
1156
1210
 
1157
1211
  // src/index.ts
1158
1212
  import { createPubSub } from "graphql-yoga";
1213
+
1214
+ // src/plugins/use-pages/setup/index.tsx
1215
+ import fs2 from "fs";
1216
+ import path3 from "path";
1217
+ import reactServer from "react-dom/server";
1218
+ import { Readable } from "stream";
1219
+
1220
+ // src/plugins/use-pages/setup/app-loader.tsx
1221
+ import { useMemo } from "react";
1222
+ import { jsx } from "react/jsx-runtime";
1223
+ var AppLoader = (props) => {
1224
+ props.client.useHydrateCache({ cacheSnapshot: props.pylonData.cacheSnapshot });
1225
+ const data = props.client.useQuery();
1226
+ const page = useMemo(() => {
1227
+ const page2 = /* @__PURE__ */ jsx(
1228
+ props.App,
1229
+ {
1230
+ pageProps: {
1231
+ ...props.pylonData.pageProps,
1232
+ data
1233
+ }
1234
+ }
1235
+ );
1236
+ return page2;
1237
+ }, [props]);
1238
+ return /* @__PURE__ */ jsx(props.Router, { ...props.routerProps, children: page });
1239
+ };
1240
+
1241
+ // src/plugins/use-pages/setup/index.tsx
1242
+ import { trimTrailingSlash } from "hono/trailing-slash";
1243
+ import { StaticRouter } from "react-router";
1244
+ import sharp from "sharp";
1245
+ import { createHash } from "crypto";
1246
+ import { tmpdir } from "os";
1247
+ import { pipeline } from "stream/promises";
1248
+ import { jsx as jsx2 } from "react/jsx-runtime";
1249
+ var disableCacheMiddleware = async (c, next) => {
1250
+ if (c.env.NODE_ENV === "development") {
1251
+ c.header(
1252
+ "Cache-Control",
1253
+ "no-store, no-cache, must-revalidate, proxy-revalidate"
1254
+ );
1255
+ c.header("Pragma", "no-cache");
1256
+ c.header("Expires", "0");
1257
+ c.header("Surrogate-Control", "no-store");
1258
+ }
1259
+ return next();
1260
+ };
1261
+ var setup = (app2) => {
1262
+ const pagesFilePath = path3.resolve(process.cwd(), ".pylon", "pages.json");
1263
+ let pageRoutes = [];
1264
+ try {
1265
+ pageRoutes = JSON.parse(fs2.readFileSync(pagesFilePath, "utf-8"));
1266
+ } catch (error) {
1267
+ console.error("Error reading pages.json", error);
1268
+ }
1269
+ app2.use(trimTrailingSlash());
1270
+ let App = void 0;
1271
+ let client = void 0;
1272
+ app2.on(
1273
+ "GET",
1274
+ pageRoutes.map((pageRoute) => pageRoute.slug),
1275
+ disableCacheMiddleware,
1276
+ async (c) => {
1277
+ if (!App) {
1278
+ const module = await import(`${process.cwd()}/.pylon/__pylon/pages/app.js`);
1279
+ App = module.default;
1280
+ }
1281
+ if (!client) {
1282
+ client = await import(`${process.cwd()}/.pylon/client`);
1283
+ }
1284
+ const pageProps = {
1285
+ params: c.req.param(),
1286
+ searchParams: c.req.query(),
1287
+ path: c.req.path
1288
+ };
1289
+ let cacheSnapshot = void 0;
1290
+ const prepared = await client.prepareReactRender(
1291
+ /* @__PURE__ */ jsx2(
1292
+ AppLoader,
1293
+ {
1294
+ Router: StaticRouter,
1295
+ routerProps: {
1296
+ location: c.req.path
1297
+ },
1298
+ App,
1299
+ client,
1300
+ pylonData: {
1301
+ pageProps,
1302
+ cacheSnapshot: void 0
1303
+ }
1304
+ }
1305
+ )
1306
+ );
1307
+ cacheSnapshot = prepared.cacheSnapshot;
1308
+ const stream = await reactServer.renderToReadableStream(
1309
+ /* @__PURE__ */ jsx2(
1310
+ AppLoader,
1311
+ {
1312
+ Router: StaticRouter,
1313
+ routerProps: {
1314
+ location: c.req.path
1315
+ },
1316
+ App,
1317
+ client,
1318
+ pylonData: {
1319
+ pageProps,
1320
+ cacheSnapshot: prepared.cacheSnapshot
1321
+ }
1322
+ }
1323
+ ),
1324
+ {
1325
+ bootstrapModules: ["/__pylon/static/app.js"],
1326
+ bootstrapScriptContent: `window.__PYLON_DATA__ = ${JSON.stringify({
1327
+ pageProps,
1328
+ cacheSnapshot
1329
+ })}`
1330
+ }
1331
+ );
1332
+ return c.body(stream);
1333
+ }
1334
+ );
1335
+ app2.get("/__pylon/static/*", disableCacheMiddleware, async (c) => {
1336
+ const filePath = path3.resolve(
1337
+ process.cwd(),
1338
+ ".pylon",
1339
+ "__pylon",
1340
+ "static",
1341
+ c.req.path.replace("/__pylon/static/", "")
1342
+ );
1343
+ if (!fs2.existsSync(filePath)) {
1344
+ throw new Error("File not found");
1345
+ }
1346
+ if (filePath.endsWith(".js")) {
1347
+ c.res.headers.set("Content-Type", "text/javascript");
1348
+ } else if (filePath.endsWith(".css")) {
1349
+ c.res.headers.set("Content-Type", "text/css");
1350
+ } else if (filePath.endsWith(".html")) {
1351
+ c.res.headers.set("Content-Type", "text/html");
1352
+ } else if (filePath.endsWith(".json")) {
1353
+ c.res.headers.set("Content-Type", "application/json");
1354
+ } else if (filePath.endsWith(".png")) {
1355
+ c.res.headers.set("Content-Type", "image/png");
1356
+ } else if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg")) {
1357
+ c.res.headers.set("Content-Type", "image/jpeg");
1358
+ } else if (filePath.endsWith(".gif")) {
1359
+ c.res.headers.set("Content-Type", "image/gif");
1360
+ } else if (filePath.endsWith(".svg")) {
1361
+ c.res.headers.set("Content-Type", "image/svg+xml");
1362
+ } else if (filePath.endsWith(".ico")) {
1363
+ c.res.headers.set("Content-Type", "image/x-icon");
1364
+ }
1365
+ const stream = fs2.createReadStream(filePath);
1366
+ const a = Readable.toWeb(stream);
1367
+ return c.body(a);
1368
+ });
1369
+ app2.get("/__pylon/image", async (c) => {
1370
+ console.log("image optimization route");
1371
+ try {
1372
+ const { src, w, h, q = "75", format = "webp" } = c.req.query();
1373
+ const queryStringHash = createHash("sha256").update(JSON.stringify(c.req.query())).digest("hex");
1374
+ if (!src) {
1375
+ return c.json({ error: "Missing parameters." }, 400);
1376
+ }
1377
+ let imagePath = path3.join(process.cwd(), ".pylon", src);
1378
+ if (src.startsWith("http://") || src.startsWith("https://")) {
1379
+ imagePath = await downloadImage(src);
1380
+ }
1381
+ try {
1382
+ console.log("imagePath", imagePath);
1383
+ await fs2.promises.access(imagePath);
1384
+ } catch {
1385
+ return c.json({ error: "Image not found" }, 404);
1386
+ }
1387
+ const metadata = await sharp(imagePath).metadata();
1388
+ if (!metadata.width || !metadata.height) {
1389
+ return c.json(
1390
+ {
1391
+ error: "Invalid image metadata. Width and height are required for resizing."
1392
+ },
1393
+ 400
1394
+ );
1395
+ }
1396
+ const { width: finalWidth, height: finalHeight } = calculateDimensions(
1397
+ metadata.width,
1398
+ metadata.height,
1399
+ w ? parseInt(w) : void 0,
1400
+ h ? parseInt(h) : void 0
1401
+ );
1402
+ const cachePath = path3.join(IMAGE_CACHE_DIR, queryStringHash);
1403
+ let imageFormat = format.toLowerCase();
1404
+ if (!isSupportedFormat(imageFormat)) {
1405
+ throw new Error("Unsupported image format");
1406
+ }
1407
+ const quality = parseInt(q);
1408
+ console.log("quality", quality);
1409
+ const image = await sharp(imagePath).resize(finalWidth, finalHeight).toFormat(imageFormat, {
1410
+ quality
1411
+ }).toFile(cachePath);
1412
+ console.log("image", image);
1413
+ c.res.headers.set("Content-Type", getContentType(image.format));
1414
+ return c.body(
1415
+ Readable.toWeb(fs2.createReadStream(cachePath))
1416
+ );
1417
+ } catch (error) {
1418
+ console.error("Error processing the image:", error);
1419
+ return c.json({ error: "Error processing the image" }, 500);
1420
+ }
1421
+ });
1422
+ };
1423
+ var IMAGE_CACHE_DIR = path3.join(process.cwd(), ".cache/__pylon/images");
1424
+ fs2.promises.mkdir(IMAGE_CACHE_DIR, { recursive: true });
1425
+ var calculateDimensions = (originalWidth, originalHeight, width, height) => {
1426
+ if (!width && !height) {
1427
+ return { width: originalWidth, height: originalHeight };
1428
+ }
1429
+ if (width && !height) {
1430
+ height = Math.round(width * originalHeight / originalWidth);
1431
+ } else if (height && !width) {
1432
+ width = Math.round(height * originalWidth / originalHeight);
1433
+ }
1434
+ return { width, height };
1435
+ };
1436
+ function isSupportedFormat(format) {
1437
+ const supportedFormats = sharp.format;
1438
+ return Object.keys(supportedFormats).includes(format);
1439
+ }
1440
+ var getContentType = (format) => {
1441
+ switch (format.toLowerCase()) {
1442
+ case "webp":
1443
+ return "image/webp";
1444
+ case "jpeg":
1445
+ case "jpg":
1446
+ return "image/jpeg";
1447
+ case "png":
1448
+ return "image/png";
1449
+ case "gif":
1450
+ return "image/gif";
1451
+ case "svg":
1452
+ return "image/svg+xml";
1453
+ default:
1454
+ return "application/octet-stream";
1455
+ }
1456
+ };
1457
+ var downloadImage = async (url) => {
1458
+ const response = await fetch(url);
1459
+ if (!response.ok)
1460
+ throw new Error(`Failed to download image: ${response.statusText}`);
1461
+ const ext = path3.extname(new URL(url).pathname) || ".jpg";
1462
+ const tempFilePath = path3.join(tmpdir(), `image-${Date.now()}${ext}`);
1463
+ const fileStream = fs2.createWriteStream(tempFilePath);
1464
+ await pipeline(response.body, fileStream);
1465
+ return tempFilePath;
1466
+ };
1467
+
1468
+ // src/plugins/use-pages/build/index.ts
1469
+ import path7 from "path";
1470
+
1471
+ // src/plugins/use-pages/build/app-utils.ts
1472
+ import path4 from "path";
1473
+ import glob from "tiny-glob";
1474
+ function fnv1aHash(str) {
1475
+ let hash = 2166136261;
1476
+ for (let i = 0; i < str.length; i++) {
1477
+ hash ^= str.charCodeAt(i);
1478
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
1479
+ }
1480
+ return (hash >>> 0).toString(16);
1481
+ }
1482
+ var APP_DIR = path4.join(process.cwd(), "pages");
1483
+ async function getPageRoutes(dir = APP_DIR) {
1484
+ const routes = [];
1485
+ const pagePattern = path4.join(dir, "**/page.{ts,tsx,js}");
1486
+ const layoutPattern = path4.join(dir, "**/layout.tsx");
1487
+ const pageFiles = await glob(pagePattern);
1488
+ const layoutFiles = await glob(layoutPattern);
1489
+ for (const pagePath of pageFiles) {
1490
+ const relativePagePath = path4.relative(APP_DIR, pagePath);
1491
+ let slug = "/" + relativePagePath.replace(/page\.(ts|tsx|js)$/, "").replace(/\[([\w-]+)\]/g, ":$1");
1492
+ slug = slug.replace(/\/$/, "");
1493
+ const layouts = layoutFiles.filter((layout) => {
1494
+ return pagePath.startsWith(layout.replace("layout.tsx", ""));
1495
+ });
1496
+ const layoutsWithoutRootLayout = layouts.slice(1);
1497
+ routes.push({
1498
+ pagePath,
1499
+ slug: slug || "/",
1500
+ layouts: layoutsWithoutRootLayout
1501
+ });
1502
+ }
1503
+ return routes;
1504
+ }
1505
+ var generateAppFile = (pageRoutes) => {
1506
+ const makePageMap = (routes) => {
1507
+ const pageMap2 = {};
1508
+ for (const route of routes) {
1509
+ pageMap2[route.pagePath] = `Page${fnv1aHash(route.pagePath)}`;
1510
+ }
1511
+ return pageMap2;
1512
+ };
1513
+ const makeLayoutMap = (routes) => {
1514
+ const layoutMap2 = {};
1515
+ for (const route of routes) {
1516
+ for (const layout of route.layouts) {
1517
+ layoutMap2[layout] = `Layout${fnv1aHash(layout)}`;
1518
+ }
1519
+ }
1520
+ return layoutMap2;
1521
+ };
1522
+ const pageMap = makePageMap(pageRoutes);
1523
+ const layoutMap = makeLayoutMap(pageRoutes);
1524
+ const importPages = Object.keys(pageMap).map((pagePath, index) => {
1525
+ const importLocation = `../${pagePath}`.replace(".tsx", ".js");
1526
+ const componentName = pageMap[pagePath];
1527
+ return `const ${componentName} = lazy(() => import('${importLocation}'))
1528
+ `;
1529
+ }).join("\n");
1530
+ const importLayouts = Object.keys(layoutMap).map((layoutPath, index) => {
1531
+ const importLocation = `../${layoutPath}`.replace(".tsx", ".js");
1532
+ const componentName = layoutMap[layoutPath];
1533
+ return `const ${componentName} = lazy(() => import('${importLocation}'))
1534
+ `;
1535
+ }).join("\n");
1536
+ const appComponent = `"use client";
1537
+ import {lazy, Suspense} from 'react'
1538
+ import { Routes, Route } from 'react-router';
1539
+ ${importPages}
1540
+ const RootLayout = lazy(() => import('../pages/layout.js'))
1541
+ ${importLayouts}
1542
+
1543
+ const App: React.FC<{pageProps: any}> = ({pageProps}) => (
1544
+ <RootLayout>
1545
+ <meta charSet="utf-8" />
1546
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1547
+ <link rel="stylesheet" href="/__pylon/static/app.css" />
1548
+ <Routes>
1549
+ ${pageRoutes.map((route, index) => {
1550
+ return `<Route key={${index}} index={${index === 0 ? "true" : "false"}} path="${route.slug}" element={
1551
+ <Suspense fallback={<div>...</div>}>
1552
+ ${route.layouts.reduceRight((child, layoutPath, layoutIndex) => {
1553
+ const layoutName = layoutMap[layoutPath];
1554
+ return `<${layoutName}>${child}</${layoutName}>`;
1555
+ }, `<${pageMap[route.pagePath]} {...pageProps} />`)}
1556
+
1557
+ </Suspense>} />`;
1558
+ }).join("\n")}
1559
+ </Routes>
1560
+ </RootLayout>
1561
+ );
1562
+
1563
+ export default App;
1564
+ `;
1565
+ return appComponent;
1566
+ };
1567
+
1568
+ // src/plugins/use-pages/build/index.ts
1569
+ import chokidar from "chokidar";
1570
+ import fs6 from "fs/promises";
1571
+ import esbuild from "esbuild";
1572
+
1573
+ // src/plugins/use-pages/build/plugins/inject-app-hydration.ts
1574
+ import path5 from "path";
1575
+ import fs3 from "fs/promises";
1576
+ var injectAppHydrationPlugin = {
1577
+ name: "inject-hydration",
1578
+ setup(build2) {
1579
+ build2.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
1580
+ if (args.path === path5.resolve(process.cwd(), ".pylon", "app.tsx")) {
1581
+ let contents = await fs3.readFile(args.path, "utf-8");
1582
+ const clientPath = path5.resolve(process.cwd(), ".pylon/client");
1583
+ const pathToClient = path5.relative(path5.dirname(args.path), clientPath);
1584
+ contents += `
1585
+ import {hydrateRoot} from 'react-dom/client'
1586
+ import * as client from './${pathToClient}'
1587
+ import {BrowserRouter} from 'react-router'
1588
+ import React, {useMemo} from 'react'
1589
+
1590
+ const pylonData = window.__PYLON_DATA__
1591
+
1592
+ console.log('pylonData', pylonData)
1593
+
1594
+ const AppLoader = (props: {
1595
+ client: any
1596
+ pylonData: {
1597
+ pageProps: Omit<PageProps, 'data'>
1598
+ cacheSnapshot?: any
1599
+ }
1600
+ App: React.FC<{
1601
+ pageProps: PageProps
1602
+ }>
1603
+ Router: React.FC<any>
1604
+ routerProps: any
1605
+ }) => {
1606
+ props.client.useHydrateCache({cacheSnapshot: props.pylonData.cacheSnapshot})
1607
+
1608
+ const data = props.client.useQuery()
1609
+ const page = useMemo(() => {
1610
+ const page = (
1611
+ <props.App
1612
+ pageProps={{
1613
+ ...props.pylonData.pageProps,
1614
+ data
1615
+ }}
1616
+ />
1617
+ )
1618
+
1619
+ return page
1620
+ }, [props])
1621
+
1622
+ return <props.Router {...props.routerProps}>{page}</props.Router>
1623
+ }
1624
+
1625
+
1626
+ hydrateRoot(
1627
+ document,
1628
+ <AppLoader Router={BrowserRouter} client={client} pylonData={pylonData} App={App} />
1629
+ )
1630
+ `;
1631
+ return {
1632
+ loader: "tsx",
1633
+ contents
1634
+ };
1635
+ }
1636
+ });
1637
+ }
1638
+ };
1639
+
1640
+ // src/plugins/use-pages/build/plugins/image-plugin.ts
1641
+ import { createHash as createHash2 } from "crypto";
1642
+ import path6 from "path";
1643
+ import sharp2 from "sharp";
1644
+ import fs4 from "fs/promises";
1645
+ var imagePlugin = {
1646
+ name: "image-plugin",
1647
+ setup(build2) {
1648
+ const outdir = build2.initialOptions.outdir;
1649
+ const publicPath = build2.initialOptions.publicPath;
1650
+ if (!outdir || !publicPath) {
1651
+ throw new Error("outdir and publicPath must be set in esbuild options");
1652
+ }
1653
+ build2.onResolve({ filter: /\.(png|jpe?g)$/ }, async (args) => {
1654
+ const filePath = path6.resolve(args.resolveDir, args.path);
1655
+ const fileName = path6.basename(filePath);
1656
+ const extname = path6.extname(filePath);
1657
+ const hash = createHash2("md5").update(filePath + await fs4.readFile(filePath)).digest("hex").slice(0, 8);
1658
+ const newFilename = `${fileName}-${hash}${extname}`;
1659
+ const newFilePath = path6.join(outdir, "media", newFilename);
1660
+ await fs4.mkdir(path6.dirname(newFilePath), { recursive: true });
1661
+ await fs4.copyFile(filePath, newFilePath);
1662
+ return {
1663
+ path: newFilePath,
1664
+ namespace: "image"
1665
+ };
1666
+ });
1667
+ build2.onLoad({ filter: /\.png$|\.jpg$/ }, async (args) => {
1668
+ const image = sharp2(args.path);
1669
+ const metadata = await image.metadata();
1670
+ const url = `${publicPath}/media/${path6.basename(args.path)}`;
1671
+ const searchParams = new URLSearchParams({});
1672
+ if (metadata.width) {
1673
+ searchParams.set("w", metadata.width.toString());
1674
+ }
1675
+ if (metadata.height) {
1676
+ searchParams.set("h", metadata.height.toString());
1677
+ }
1678
+ const output = image.resize({
1679
+ width: Math.min(metadata.width ?? 16, 16),
1680
+ height: Math.min(metadata.height ?? 16, 16),
1681
+ fit: "inside"
1682
+ }).toFormat("webp", {
1683
+ quality: 20,
1684
+ alphaQuality: 20,
1685
+ smartSubsample: true
1686
+ });
1687
+ const { data, info } = await output.toBuffer({ resolveWithObject: true });
1688
+ const dataURIBase64 = `data:image/${info.format};base64,${data.toString(
1689
+ "base64"
1690
+ )}`;
1691
+ if (dataURIBase64) {
1692
+ searchParams.set("blurDataURL", dataURIBase64);
1693
+ }
1694
+ return {
1695
+ contents: `${url}?${searchParams.toString()}`,
1696
+ loader: "text"
1697
+ };
1698
+ });
1699
+ }
1700
+ };
1701
+
1702
+ // src/plugins/use-pages/build/plugins/postcss-plugin.ts
1703
+ import fs5 from "fs/promises";
1704
+ import loadConfig from "postcss-load-config";
1705
+ import postcss from "postcss";
1706
+ var postcssPlugin = {
1707
+ name: "postcss-plugin",
1708
+ setup(build2) {
1709
+ build2.onLoad({ filter: /.css$/, namespace: "file" }, async (args) => {
1710
+ const { plugins, options } = await loadConfig();
1711
+ const css = await fs5.readFile(args.path, "utf-8");
1712
+ const result = await postcss(plugins).process(css, {
1713
+ ...options,
1714
+ from: args.path
1715
+ }).then((result2) => result2);
1716
+ return {
1717
+ contents: result.css,
1718
+ loader: "css"
1719
+ };
1720
+ });
1721
+ }
1722
+ };
1723
+
1724
+ // src/plugins/use-pages/build/index.ts
1725
+ var DIST_STATIC_DIR = path7.join(process.cwd(), ".pylon/__pylon/static");
1726
+ var DIST_PAGES_DIR = path7.join(process.cwd(), ".pylon/__pylon/pages");
1727
+ async function updateFileIfChanged(path8, newContent) {
1728
+ try {
1729
+ const currentContent = await fs6.readFile(path8, "utf8");
1730
+ if (currentContent === newContent) {
1731
+ return false;
1732
+ }
1733
+ } catch (err) {
1734
+ if (err.code !== "ENOENT") throw err;
1735
+ }
1736
+ await fs6.writeFile(path8, newContent, "utf8");
1737
+ return true;
1738
+ }
1739
+ var build = async () => {
1740
+ const buildAppFile = async () => {
1741
+ const pagesRoutes = await getPageRoutes();
1742
+ const appContent = generateAppFile(pagesRoutes);
1743
+ const appFilePath = path7.resolve(process.cwd(), ".pylon", "app.tsx");
1744
+ const state = await updateFileIfChanged(appFilePath, appContent);
1745
+ if (state) {
1746
+ console.log("Updated app file");
1747
+ }
1748
+ };
1749
+ const writeOnEndPlugin = {
1750
+ name: "write-on-end",
1751
+ setup(build2) {
1752
+ build2.onEnd(async (result) => {
1753
+ result.outputFiles?.forEach(async (file) => {
1754
+ await fs6.mkdir(path7.dirname(file.path), { recursive: true });
1755
+ await updateFileIfChanged(file.path, file.text);
1756
+ });
1757
+ });
1758
+ }
1759
+ };
1760
+ let pagesWatcher = null;
1761
+ const clientCtx = await esbuild.context({
1762
+ write: false,
1763
+ metafile: true,
1764
+ absWorkingDir: process.cwd(),
1765
+ plugins: [
1766
+ injectAppHydrationPlugin,
1767
+ imagePlugin,
1768
+ postcssPlugin,
1769
+ writeOnEndPlugin
1770
+ ],
1771
+ publicPath: "/__pylon/static",
1772
+ assetNames: "assets/[name]-[hash]",
1773
+ chunkNames: "chunks/[name]-[hash]",
1774
+ format: "esm",
1775
+ platform: "browser",
1776
+ entryPoints: [".pylon/app.tsx"],
1777
+ outdir: DIST_STATIC_DIR,
1778
+ bundle: true,
1779
+ splitting: true,
1780
+ minify: false,
1781
+ loader: {
1782
+ // Map file extensions to the file loader
1783
+ ".svg": "file",
1784
+ ".woff": "file",
1785
+ ".woff2": "file"
1786
+ },
1787
+ define: {
1788
+ "process.env.NODE_ENV": '"production"'
1789
+ },
1790
+ mainFields: ["browser", "module", "main"]
1791
+ });
1792
+ const serverCtx = await esbuild.context({
1793
+ write: false,
1794
+ absWorkingDir: process.cwd(),
1795
+ plugins: [imagePlugin, postcssPlugin, writeOnEndPlugin],
1796
+ publicPath: "/__pylon/static",
1797
+ assetNames: "assets/[name]-[hash]",
1798
+ chunkNames: "chunks/[name]-[hash]",
1799
+ format: "esm",
1800
+ platform: "node",
1801
+ entryPoints: [".pylon/app.tsx"],
1802
+ outdir: DIST_PAGES_DIR,
1803
+ bundle: true,
1804
+ packages: "external",
1805
+ splitting: false,
1806
+ minify: false,
1807
+ loader: {
1808
+ // Map file extensions to the file loader
1809
+ ".svg": "file",
1810
+ ".woff": "file",
1811
+ ".woff2": "file"
1812
+ }
1813
+ });
1814
+ return {
1815
+ watch: async () => {
1816
+ console.log("Watching pages directory...");
1817
+ pagesWatcher = chokidar.watch("pages", { ignoreInitial: false });
1818
+ pagesWatcher.on("all", (event, path8) => {
1819
+ if (["add", "change", "unlink"].includes(event)) {
1820
+ buildAppFile();
1821
+ }
1822
+ });
1823
+ await Promise.all([clientCtx.watch(), serverCtx.watch()]);
1824
+ },
1825
+ dispose: async () => {
1826
+ console.log("Disposing pages");
1827
+ if (pagesWatcher) {
1828
+ pagesWatcher.close();
1829
+ }
1830
+ Promise.all([clientCtx.dispose(), serverCtx.dispose()]);
1831
+ },
1832
+ rebuild: async () => {
1833
+ console.log("Rebuilding pages");
1834
+ await buildAppFile();
1835
+ await Promise.all([clientCtx.rebuild(), serverCtx.rebuild()]);
1836
+ return {};
1837
+ },
1838
+ cancel: async () => {
1839
+ if (pagesWatcher) {
1840
+ await pagesWatcher.close();
1841
+ }
1842
+ await Promise.all([clientCtx.cancel(), serverCtx.cancel()]);
1843
+ }
1844
+ };
1845
+ };
1846
+
1847
+ // src/plugins/use-pages/index.ts
1848
+ function usePages() {
1849
+ return {
1850
+ setup,
1851
+ build
1852
+ };
1853
+ }
1159
1854
  export {
1160
1855
  ServiceError,
1161
1856
  app,
1162
1857
  asyncContext,
1163
- auth,
1858
+ authMiddleware,
1164
1859
  createDecorator,
1165
1860
  createPubSub as experimentalCreatePubSub,
1166
1861
  getContext,
1167
1862
  getEnv,
1168
1863
  handler,
1169
1864
  requireAuth,
1170
- setContext
1865
+ setContext,
1866
+ useAuth,
1867
+ usePages
1171
1868
  };
1172
1869
  //# sourceMappingURL=index.js.map