@noego/forge 0.0.7 → 0.0.9

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 (36) hide show
  1. package/README.md +5 -1
  2. package/dist/client.cjs.map +1 -1
  3. package/dist/client.d.ts +5 -0
  4. package/dist/client.mjs +2 -2
  5. package/dist/client.mjs.map +1 -1
  6. package/dist/index.plugins.cjs +9 -0
  7. package/dist/index.plugins.cjs.map +1 -0
  8. package/dist/index.plugins.d.ts +2 -0
  9. package/dist/index.plugins.js +2 -0
  10. package/dist/index.plugins.js.map +1 -0
  11. package/dist/page.mjs +1 -1
  12. package/dist/{page.svelte-C4chAYK2.js → page.svelte-Bq1Q01H0.js} +2 -7
  13. package/dist/{page.svelte-C4chAYK2.js.map → page.svelte-Bq1Q01H0.js.map} +1 -1
  14. package/dist/page.svelte-Dvj7306U.cjs.map +1 -1
  15. package/dist/plugins/serverOnlyStub.cjs +226 -0
  16. package/dist/plugins/serverOnlyStub.cjs.map +1 -0
  17. package/dist/plugins/serverOnlyStub.js +200 -0
  18. package/dist/plugins/serverOnlyStub.js.map +1 -0
  19. package/dist/shared.mjs +1 -1
  20. package/dist/stubs/server-only.d.ts +3 -0
  21. package/dist/stubs/server-only.js +7 -0
  22. package/dist/stubs/server-only.js.map +1 -0
  23. package/dist-ssr/{path-ODk1FhWY.js → path-9twSsimy.js} +5 -2
  24. package/dist-ssr/{path-ODk1FhWY.js.map → path-9twSsimy.js.map} +1 -1
  25. package/dist-ssr/{path-CyGuWUeq.cjs → path-Dm_4PXDW.cjs} +5 -2
  26. package/dist-ssr/{path-CyGuWUeq.cjs.map → path-Dm_4PXDW.cjs.map} +1 -1
  27. package/dist-ssr/server.cjs +249 -74
  28. package/dist-ssr/server.cjs.map +1 -1
  29. package/dist-ssr/server.d.ts +6 -1
  30. package/dist-ssr/server.js +244 -69
  31. package/dist-ssr/server.js.map +1 -1
  32. package/dist-ssr/shared.cjs +2 -7
  33. package/dist-ssr/shared.cjs.map +1 -1
  34. package/dist-ssr/shared.js +2 -7
  35. package/dist-ssr/shared.js.map +1 -1
  36. package/package.json +12 -4
@@ -33,7 +33,8 @@ const fs$1 = require("fs/promises");
33
33
  const yaml = require("js-yaml");
34
34
  require("clone-deep");
35
35
  const join = require("url-join");
36
- const path$1 = require("./path-CyGuWUeq.cjs");
36
+ const path$1 = require("./path-Dm_4PXDW.cjs");
37
+ const url = require("url");
37
38
  const server = require("svelte/server");
38
39
  const pathToRegex = require("path-to-regex");
39
40
  const ApplicationRenderer = require("../src/components/RecursiveRender.svelte");
@@ -140,6 +141,23 @@ class LiveHTMLRender {
140
141
  return file_content;
141
142
  }
142
143
  }
144
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
145
+ "get",
146
+ "post",
147
+ "put",
148
+ "delete",
149
+ "patch",
150
+ "head",
151
+ "options",
152
+ "trace"
153
+ ]);
154
+ function getGlobalPathsMiddleware(openapi) {
155
+ if (!openapi || !openapi.paths) {
156
+ return [];
157
+ }
158
+ const value = openapi.paths["x-middleware"];
159
+ return Array.isArray(value) ? [...value] : [];
160
+ }
143
161
  function parseConfigfile(file_content) {
144
162
  let config = null;
145
163
  try {
@@ -149,7 +167,7 @@ function parseConfigfile(file_content) {
149
167
  }
150
168
  return config;
151
169
  }
152
- function parse_modules(openapi) {
170
+ function parse_modules(openapi, inheritedMiddleware = []) {
153
171
  const modules_config = openapi.modules || openapi.module;
154
172
  if (!modules_config) {
155
173
  return;
@@ -157,16 +175,20 @@ function parse_modules(openapi) {
157
175
  const modules = Object.entries(modules_config).map(([_, module2]) => {
158
176
  return module2;
159
177
  });
178
+ const globalMiddleware = [...inheritedMiddleware, ...getGlobalPathsMiddleware(openapi)];
160
179
  const modules_path = modules.map((module2) => {
161
180
  const basePath = module2.basePath || "";
162
181
  const baseLayouts = module2.baseLayouts || [];
182
+ const moduleMiddleware = Array.isArray(module2["x-middleware"]) ? module2["x-middleware"] : [];
183
+ const inherited = [...globalMiddleware, ...moduleMiddleware];
163
184
  const paths = module2.paths;
164
185
  if (!paths) {
165
186
  return;
166
187
  }
167
- const configurations = Object.entries(paths).map(([path2, method_config]) => {
168
- return Object.entries(method_config).map(([method, config]) => {
169
- return path$1.parsePathConfig(path2, method, config);
188
+ const configurations = Object.entries(paths).filter(([path2]) => typeof path2 === "string" && path2.startsWith("/")).map(([path2, method_config]) => {
189
+ return Object.entries(method_config).filter(([method]) => HTTP_METHODS.has(String(method).toLowerCase())).map(([method, config]) => {
190
+ const methodName = String(method);
191
+ return path$1.parsePathConfig(path2, methodName, config, inherited);
170
192
  });
171
193
  });
172
194
  const routes = configurations.reduce((flat_config, config) => {
@@ -180,19 +202,24 @@ function parse_modules(openapi) {
180
202
  });
181
203
  return modules_path.reduce(
182
204
  (flat_config, config) => {
205
+ if (!config) {
206
+ return flat_config;
207
+ }
183
208
  return flat_config.concat(config);
184
209
  },
185
210
  []
186
211
  );
187
212
  }
188
- function parse_paths(openapi) {
213
+ function parse_paths(openapi, inheritedMiddleware = []) {
189
214
  const paths = openapi.paths;
190
215
  if (!paths) {
191
216
  return;
192
217
  }
193
- const configurations = Object.entries(paths).map(([path2, method_config]) => {
194
- return Object.entries(method_config).map(([method, config]) => {
195
- return path$1.parsePathConfig(path2, method, config);
218
+ const globalMiddleware = [...inheritedMiddleware, ...getGlobalPathsMiddleware(openapi)];
219
+ const configurations = Object.entries(paths).filter(([path2]) => typeof path2 === "string" && path2.startsWith("/")).map(([path2, method_config]) => {
220
+ return Object.entries(method_config).filter(([method]) => HTTP_METHODS.has(String(method).toLowerCase())).map(([method, config]) => {
221
+ const methodName = String(method);
222
+ return path$1.parsePathConfig(path2, methodName, config, globalMiddleware);
196
223
  });
197
224
  });
198
225
  const routes = configurations.reduce((flat_config, config) => {
@@ -232,13 +259,16 @@ async function parse_openapi_config(openapi_config_path) {
232
259
  } else {
233
260
  throw new Error(`Invalid OpenAPI or stitch configuration file: ${openapi_config_path}. File must contain either 'stitch', 'paths', 'module', or 'modules' at the root level.`);
234
261
  }
262
+ const globalPathsMiddleware = getGlobalPathsMiddleware(openapi_config);
235
263
  let modules = parse_modules(openapi_config) || [];
236
264
  let paths = parse_paths(openapi_config) || [];
237
265
  let fallback_layout = openapi_config["x-fallback-layout"] || null;
238
266
  let fallback_view = openapi_config["x-fallback-view"] || null;
267
+ const fallback_middleware = Array.isArray(openapi_config["x-fallback-middleware"]) ? [...openapi_config["x-fallback-middleware"]] : [...globalPathsMiddleware];
239
268
  let fallback = {
240
269
  layout: fallback_layout ? [fallback_layout] : [],
241
- view: fallback_view
270
+ view: fallback_view,
271
+ middleware: fallback_middleware
242
272
  };
243
273
  const routes = [...modules, ...paths];
244
274
  let config = {
@@ -264,7 +294,8 @@ const defaultOptions = {
264
294
  build_dir: "dist_ssr",
265
295
  renderer: "default",
266
296
  open_api_path: path.join(process.cwd(), "openapi.yaml"),
267
- manifest_endpoint: "/manifest.json"
297
+ manifest_endpoint: "/manifest.json",
298
+ middleware_path: void 0
268
299
  };
269
300
  class ManifestBuilder {
270
301
  constructor(openapi_path, loader, options) {
@@ -404,61 +435,186 @@ class ComponentManager {
404
435
  return view.default;
405
436
  }
406
437
  }
438
+ const EXTENSION_CANDIDATES = [".js", ".mjs", ".cjs", ".ts", ".tsx"];
407
439
  class MiddlewareAdapter {
408
- constructor(manager) {
409
- this.manager = manager;
440
+ constructor(middlewarePath, fallbackPath) {
441
+ __publicField(this, "cache", /* @__PURE__ */ new Map());
442
+ this.middlewarePath = middlewarePath;
443
+ this.fallbackPath = fallbackPath;
410
444
  }
411
- async handleMiddleware(req, reply, route, request_data, callback) {
412
- let components = await this.manager.getLayoutComponents(route);
413
- let middleware_stack = await Promise.all(components.map(async (component) => {
414
- const middleware = [...component.middleware || [], ...component.middlewares || []];
415
- return middleware;
416
- }));
417
- const layout_middlewares = middleware_stack.flat() || [];
418
- console.log("middleware stack loaded", layout_middlewares);
419
- req.params = request_data.params;
420
- await new Promise((resolve) => {
421
- this.runMiddleware(layout_middlewares, req, reply, resolve);
422
- });
423
- console.log("layout middleware executed");
424
- console.log("view middleware loaded");
425
- console.log("view middleware", route.view);
426
- let view = {};
427
- if (route.view) {
428
- view = await this.manager.getViewComponent(route);
429
- }
430
- let view_middlewares = [...view.middleware || [], ...view.middlewares || []];
431
- await new Promise((resolve) => {
432
- this.runMiddleware(view_middlewares, req, reply, resolve);
433
- });
434
- callback();
445
+ static build(middlewarePath, fallbackPath) {
446
+ return new MiddlewareAdapter(middlewarePath, fallbackPath);
435
447
  }
436
- runMiddleware(middleware, req, reply, callback) {
437
- middleware = this.dedupe(middleware);
438
- let index = 0;
439
- if (middleware.length === 0) {
440
- callback();
448
+ async handleMiddleware(req, res, middlewareConfig = []) {
449
+ if (!Array.isArray(middlewareConfig) || middlewareConfig.length === 0) {
441
450
  return;
442
451
  }
443
- let next = () => {
444
- if (index >= middleware.length) {
445
- callback();
446
- return;
452
+ const resolution = await this.resolveMiddlewareFunctions(middlewareConfig);
453
+ console.log(resolution);
454
+ if (resolution.errors.length > 0) {
455
+ throw new Error(`Middleware resolution failed: ${resolution.errors.join(", ")}`);
456
+ }
457
+ for (const middleware of resolution.functions) {
458
+ await this.executeMiddleware(middleware, req, res);
459
+ if (res.headersSent) {
460
+ break;
447
461
  }
448
- const mw = middleware[index];
449
- index++;
450
- if (typeof mw === "function") {
451
- if (!reply.sent)
452
- mw(req, reply, next);
453
- } else {
454
- if (!reply.sent)
455
- next();
462
+ }
463
+ }
464
+ async resolveMiddlewareFunctions(middlewareConfig) {
465
+ const functions = [];
466
+ const errors = [];
467
+ for (const spec of middlewareConfig) {
468
+ if (!spec) {
469
+ continue;
456
470
  }
471
+ try {
472
+ let resolved = this.cache.get(spec);
473
+ if (!resolved) {
474
+ resolved = await this.resolveMiddlewareSpec(spec);
475
+ this.cache.set(spec, [...resolved]);
476
+ }
477
+ functions.push(...resolved);
478
+ } catch (error) {
479
+ errors.push(`${spec}: ${error.message}`);
480
+ }
481
+ }
482
+ return {
483
+ functions: this.dedupe(functions),
484
+ errors
457
485
  };
458
- next();
459
486
  }
460
- dedupe(middlewares) {
461
- return Array.from(new Set(middlewares));
487
+ async resolveMiddlewareSpec(spec) {
488
+ const [filePath, selector] = this.parseMiddlewareSpec(spec);
489
+ const fullPath = this.resolveMiddlewareFilePath(filePath);
490
+ const module2 = await this.importMiddlewareModule(fullPath, spec);
491
+ return this.extractMiddlewareFunctions(module2, selector, spec);
492
+ }
493
+ parseMiddlewareSpec(spec) {
494
+ const colonIndex = spec.indexOf(":");
495
+ if (colonIndex === -1) {
496
+ return [spec, void 0];
497
+ }
498
+ return [spec.substring(0, colonIndex), spec.substring(colonIndex + 1)];
499
+ }
500
+ resolveMiddlewareFilePath(filePath) {
501
+ const basePath = this.middlewarePath || this.fallbackPath || process.cwd();
502
+ const transformed = this.middlewareFileTransform(filePath);
503
+ const baseCandidate = path.resolve(process.cwd(), path.join(basePath, transformed));
504
+ const resolvedWithExt = this.findExistingFile(baseCandidate);
505
+ if (resolvedWithExt) {
506
+ return resolvedWithExt;
507
+ }
508
+ throw new Error(`Middleware file not found: ${transformed}`);
509
+ }
510
+ middlewareFileTransform(filePath) {
511
+ return filePath.split(".").join(path.sep);
512
+ }
513
+ findExistingFile(basePathWithoutExt) {
514
+ const ext = path.extname(basePathWithoutExt);
515
+ if (ext) {
516
+ return fs.existsSync(basePathWithoutExt) ? basePathWithoutExt : null;
517
+ }
518
+ const directCandidates = EXTENSION_CANDIDATES.map((extCandidate) => `${basePathWithoutExt}${extCandidate}`);
519
+ for (const candidate of directCandidates) {
520
+ if (fs.existsSync(candidate)) {
521
+ return candidate;
522
+ }
523
+ }
524
+ const indexCandidates = EXTENSION_CANDIDATES.map((extCandidate) => path.join(basePathWithoutExt, `index${extCandidate}`));
525
+ for (const candidate of indexCandidates) {
526
+ if (fs.existsSync(candidate)) {
527
+ return candidate;
528
+ }
529
+ }
530
+ return null;
531
+ }
532
+ async importMiddlewareModule(fullPath, spec) {
533
+ try {
534
+ const url$1 = url.pathToFileURL(fullPath).href;
535
+ return await import(url$1);
536
+ } catch (error) {
537
+ throw new Error(`Unable to import middleware '${spec}' from ${fullPath}`);
538
+ }
539
+ }
540
+ extractMiddlewareFunctions(module2, selector, spec) {
541
+ if (!selector) {
542
+ return this.extractDefaultFunction(module2, spec);
543
+ }
544
+ if (selector === "*") {
545
+ return this.extractAllFunctions(module2, spec);
546
+ }
547
+ return this.extractSpecificFunctions(module2, selector, spec);
548
+ }
549
+ extractDefaultFunction(module2, spec) {
550
+ if (!(module2 == null ? void 0 : module2.default)) {
551
+ throw new Error(`Middleware '${spec}' is missing a default export`);
552
+ }
553
+ if (typeof module2.default !== "function") {
554
+ throw new Error(`Default export for middleware '${spec}' is not a function`);
555
+ }
556
+ return [module2.default];
557
+ }
558
+ extractAllFunctions(module2, spec) {
559
+ const functions = Object.values(module2).filter((value) => typeof value === "function");
560
+ if (functions.length === 0) {
561
+ throw new Error(`No functions exported by middleware '${spec}'`);
562
+ }
563
+ return functions;
564
+ }
565
+ extractSpecificFunctions(module2, selector, spec) {
566
+ const names = selector.split(",").map((name) => name.trim()).filter(Boolean);
567
+ const functions = [];
568
+ const missing = [];
569
+ const invalid = [];
570
+ for (const name of names) {
571
+ const value = name === "default" ? module2 == null ? void 0 : module2.default : module2 == null ? void 0 : module2[name];
572
+ if (value === void 0) {
573
+ missing.push(name);
574
+ continue;
575
+ }
576
+ if (typeof value !== "function") {
577
+ invalid.push(name);
578
+ continue;
579
+ }
580
+ functions.push(value);
581
+ }
582
+ if (missing.length) {
583
+ throw new Error(`Middleware '${spec}' is missing export(s): ${missing.join(", ")}`);
584
+ }
585
+ if (invalid.length) {
586
+ throw new Error(`Middleware '${spec}' export(s) not functions: ${invalid.join(", ")}`);
587
+ }
588
+ if (functions.length === 0) {
589
+ throw new Error(`Middleware '${spec}' did not resolve any functions for selector '${selector}'`);
590
+ }
591
+ return functions;
592
+ }
593
+ executeMiddleware(fn, req, res) {
594
+ return new Promise((resolve, reject) => {
595
+ try {
596
+ fn(req, res, (error) => {
597
+ if (error) {
598
+ reject(error);
599
+ return;
600
+ }
601
+ resolve();
602
+ });
603
+ } catch (error) {
604
+ reject(error);
605
+ }
606
+ });
607
+ }
608
+ dedupe(functions) {
609
+ const seen = /* @__PURE__ */ new Set();
610
+ const ordered = [];
611
+ for (const fn of functions) {
612
+ if (!seen.has(fn)) {
613
+ seen.add(fn);
614
+ ordered.push(fn);
615
+ }
616
+ }
617
+ return ordered;
462
618
  }
463
619
  }
464
620
  function makeUrlParser(pattern) {
@@ -478,13 +634,14 @@ function initialize_route_matchers(routes) {
478
634
  class ServerAdapter {
479
635
  }
480
636
  class ExpressServerAdapter extends ServerAdapter {
481
- constructor(server2, manager, htmlRender, api_adapter, middleware_adapter) {
637
+ constructor(server2, manager, htmlRender, api_adapter, middleware_adapter, context_builder) {
482
638
  super();
483
639
  this.server = server2;
484
640
  this.manager = manager;
485
641
  this.htmlRender = htmlRender;
486
642
  this.api_adapter = api_adapter;
487
643
  this.middleware_adapter = middleware_adapter;
644
+ this.context_builder = context_builder;
488
645
  }
489
646
  async handleRoutes(routes, manifest) {
490
647
  const matchers = initialize_route_matchers(routes);
@@ -520,13 +677,20 @@ class ExpressServerAdapter extends ServerAdapter {
520
677
  return;
521
678
  });
522
679
  }
523
- async handleResponse(url, matcher, route, routes, manifest, req, res) {
680
+ async handleResponse(url2, matcher, route, routes, manifest, req, res) {
524
681
  var _a, _b;
525
682
  let accepts = req.headers.accept || "";
526
683
  let server_api_call = accepts.toLowerCase().includes("application/json");
684
+ if (res.headersSent) {
685
+ return;
686
+ }
527
687
  const context = {};
688
+ if (this.context_builder) {
689
+ const built_context = await this.context_builder(req, res);
690
+ Object.assign(context, built_context);
691
+ }
528
692
  req["context"] = context;
529
- const cleanPath = url.split("?")[0];
693
+ const cleanPath = url2.split("?")[0];
530
694
  const pathParams = matcher.parser(cleanPath);
531
695
  const queryParams = {};
532
696
  for (const [key, value] of Object.entries(req.query)) {
@@ -540,7 +704,7 @@ class ExpressServerAdapter extends ServerAdapter {
540
704
  }
541
705
  const combinedParams = { ...pathParams, ...queryParams };
542
706
  let request_data = {
543
- url,
707
+ url: url2,
544
708
  query: req.query,
545
709
  params: combinedParams,
546
710
  headers: req.headers,
@@ -548,20 +712,17 @@ class ExpressServerAdapter extends ServerAdapter {
548
712
  context
549
713
  };
550
714
  try {
551
- await new Promise((resolve, reject) => {
552
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve).catch(reject);
553
- });
715
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
554
716
  } catch (e) {
555
717
  console.log("Error in middleware", e);
556
718
  res.status(500).send(`
557
719
  <h1>500 Internal Server Error</h1>
558
720
  <pre>${e}</pre>
721
+ <pre>Stack Trace:
722
+ ${e.stack}</pre>
559
723
  `);
560
724
  return;
561
725
  }
562
- if (res.headersSent) {
563
- return;
564
- }
565
726
  let server_data = await this.api_adapter.getApiData(route, request_data);
566
727
  if (server_api_call) {
567
728
  res.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate").header("Pragma", "no-cache").header("Expires", "0").type("application/json").send(server_data);
@@ -577,6 +738,8 @@ class ExpressServerAdapter extends ServerAdapter {
577
738
  res.status(500).send(`
578
739
  <h1>500 Internal Server Error</h1>
579
740
  <pre>${e}</pre>
741
+ <pre>Stack Trace:
742
+ ${e.stack}</pre>
580
743
  `);
581
744
  return;
582
745
  }
@@ -640,10 +803,10 @@ view: ${route.view}</pre>`;
640
803
  let route = server_config.fallback;
641
804
  let accepts = req.headers.accept || "";
642
805
  let server_api_call = accepts.toLowerCase().includes("application/json");
643
- const url = req.url || "/";
806
+ const url2 = req.url || "/";
644
807
  const context = {};
645
808
  let fallback_data = {
646
- url,
809
+ url: url2,
647
810
  query: req.query,
648
811
  params: {},
649
812
  headers: req.headers,
@@ -651,9 +814,16 @@ view: ${route.view}</pre>`;
651
814
  context
652
815
  };
653
816
  let request_data = fallback_data;
654
- await new Promise((resolve) => {
655
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve);
656
- });
817
+ try {
818
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
819
+ } catch (e) {
820
+ console.log("Error in fallback middleware", e);
821
+ res.status(500).send(`
822
+ <h1>500 Internal Server Error</h1>
823
+ <pre>${e}</pre>
824
+ `);
825
+ return;
826
+ }
657
827
  console.log("Completed fallback middleware");
658
828
  let server_data = await this.api_adapter.getApiData(route, request_data);
659
829
  console.log("Completed fallback API call");
@@ -727,13 +897,18 @@ ${JSON.stringify(options, null, 2)}`);
727
897
  const routeDefs = server_config.routes;
728
898
  const manager = new ComponentManager(componentLoader);
729
899
  const api_adapter = new ApiAdapter(manager);
730
- const middleware_adapter = new MiddlewareAdapter(manager);
900
+ let middlewareRoot;
901
+ if (full_options.middleware_path) {
902
+ middlewareRoot = path.isAbsolute(full_options.middleware_path) ? full_options.middleware_path : path.resolve(full_options.viteOptions.root, full_options.middleware_path);
903
+ }
904
+ const middleware_adapter = MiddlewareAdapter.build(middlewareRoot, COMPONENT_DIR);
731
905
  const adapter = new ExpressServerAdapter(
732
906
  app,
733
907
  manager,
734
908
  htmlRenderer,
735
909
  api_adapter,
736
- middleware_adapter
910
+ middleware_adapter,
911
+ full_options.context_builder
737
912
  );
738
913
  await adapter.handleRoutes(routeDefs, manifest);
739
914
  console.log("Routes registered");