@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
@@ -10,7 +10,8 @@ import fs$1 from "fs/promises";
10
10
  import yaml from "js-yaml";
11
11
  import "clone-deep";
12
12
  import join from "url-join";
13
- import { p as parsePathConfig } from "./path-ODk1FhWY.js";
13
+ import { p as parsePathConfig } from "./path-9twSsimy.js";
14
+ import { pathToFileURL } from "url";
14
15
  import { render } from "svelte/server";
15
16
  import pathToRegex from "path-to-regex";
16
17
  import ApplicationRenderer from "../src/components/RecursiveRender.svelte";
@@ -117,6 +118,23 @@ class LiveHTMLRender {
117
118
  return file_content;
118
119
  }
119
120
  }
121
+ const HTTP_METHODS = /* @__PURE__ */ new Set([
122
+ "get",
123
+ "post",
124
+ "put",
125
+ "delete",
126
+ "patch",
127
+ "head",
128
+ "options",
129
+ "trace"
130
+ ]);
131
+ function getGlobalPathsMiddleware(openapi) {
132
+ if (!openapi || !openapi.paths) {
133
+ return [];
134
+ }
135
+ const value = openapi.paths["x-middleware"];
136
+ return Array.isArray(value) ? [...value] : [];
137
+ }
120
138
  function parseConfigfile(file_content) {
121
139
  let config = null;
122
140
  try {
@@ -126,7 +144,7 @@ function parseConfigfile(file_content) {
126
144
  }
127
145
  return config;
128
146
  }
129
- function parse_modules(openapi) {
147
+ function parse_modules(openapi, inheritedMiddleware = []) {
130
148
  const modules_config = openapi.modules || openapi.module;
131
149
  if (!modules_config) {
132
150
  return;
@@ -134,16 +152,20 @@ function parse_modules(openapi) {
134
152
  const modules = Object.entries(modules_config).map(([_, module]) => {
135
153
  return module;
136
154
  });
155
+ const globalMiddleware = [...inheritedMiddleware, ...getGlobalPathsMiddleware(openapi)];
137
156
  const modules_path = modules.map((module) => {
138
157
  const basePath = module.basePath || "";
139
158
  const baseLayouts = module.baseLayouts || [];
159
+ const moduleMiddleware = Array.isArray(module["x-middleware"]) ? module["x-middleware"] : [];
160
+ const inherited = [...globalMiddleware, ...moduleMiddleware];
140
161
  const paths = module.paths;
141
162
  if (!paths) {
142
163
  return;
143
164
  }
144
- const configurations = Object.entries(paths).map(([path2, method_config]) => {
145
- return Object.entries(method_config).map(([method, config]) => {
146
- return parsePathConfig(path2, method, config);
165
+ const configurations = Object.entries(paths).filter(([path2]) => typeof path2 === "string" && path2.startsWith("/")).map(([path2, method_config]) => {
166
+ return Object.entries(method_config).filter(([method]) => HTTP_METHODS.has(String(method).toLowerCase())).map(([method, config]) => {
167
+ const methodName = String(method);
168
+ return parsePathConfig(path2, methodName, config, inherited);
147
169
  });
148
170
  });
149
171
  const routes = configurations.reduce((flat_config, config) => {
@@ -157,19 +179,24 @@ function parse_modules(openapi) {
157
179
  });
158
180
  return modules_path.reduce(
159
181
  (flat_config, config) => {
182
+ if (!config) {
183
+ return flat_config;
184
+ }
160
185
  return flat_config.concat(config);
161
186
  },
162
187
  []
163
188
  );
164
189
  }
165
- function parse_paths(openapi) {
190
+ function parse_paths(openapi, inheritedMiddleware = []) {
166
191
  const paths = openapi.paths;
167
192
  if (!paths) {
168
193
  return;
169
194
  }
170
- const configurations = Object.entries(paths).map(([path2, method_config]) => {
171
- return Object.entries(method_config).map(([method, config]) => {
172
- return parsePathConfig(path2, method, config);
195
+ const globalMiddleware = [...inheritedMiddleware, ...getGlobalPathsMiddleware(openapi)];
196
+ const configurations = Object.entries(paths).filter(([path2]) => typeof path2 === "string" && path2.startsWith("/")).map(([path2, method_config]) => {
197
+ return Object.entries(method_config).filter(([method]) => HTTP_METHODS.has(String(method).toLowerCase())).map(([method, config]) => {
198
+ const methodName = String(method);
199
+ return parsePathConfig(path2, methodName, config, globalMiddleware);
173
200
  });
174
201
  });
175
202
  const routes = configurations.reduce((flat_config, config) => {
@@ -209,13 +236,16 @@ async function parse_openapi_config(openapi_config_path) {
209
236
  } else {
210
237
  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.`);
211
238
  }
239
+ const globalPathsMiddleware = getGlobalPathsMiddleware(openapi_config);
212
240
  let modules = parse_modules(openapi_config) || [];
213
241
  let paths = parse_paths(openapi_config) || [];
214
242
  let fallback_layout = openapi_config["x-fallback-layout"] || null;
215
243
  let fallback_view = openapi_config["x-fallback-view"] || null;
244
+ const fallback_middleware = Array.isArray(openapi_config["x-fallback-middleware"]) ? [...openapi_config["x-fallback-middleware"]] : [...globalPathsMiddleware];
216
245
  let fallback = {
217
246
  layout: fallback_layout ? [fallback_layout] : [],
218
- view: fallback_view
247
+ view: fallback_view,
248
+ middleware: fallback_middleware
219
249
  };
220
250
  const routes = [...modules, ...paths];
221
251
  let config = {
@@ -241,7 +271,8 @@ const defaultOptions = {
241
271
  build_dir: "dist_ssr",
242
272
  renderer: "default",
243
273
  open_api_path: path.join(process.cwd(), "openapi.yaml"),
244
- manifest_endpoint: "/manifest.json"
274
+ manifest_endpoint: "/manifest.json",
275
+ middleware_path: void 0
245
276
  };
246
277
  class ManifestBuilder {
247
278
  constructor(openapi_path, loader, options) {
@@ -381,61 +412,186 @@ class ComponentManager {
381
412
  return view.default;
382
413
  }
383
414
  }
415
+ const EXTENSION_CANDIDATES = [".js", ".mjs", ".cjs", ".ts", ".tsx"];
384
416
  class MiddlewareAdapter {
385
- constructor(manager) {
386
- this.manager = manager;
417
+ constructor(middlewarePath, fallbackPath) {
418
+ __publicField(this, "cache", /* @__PURE__ */ new Map());
419
+ this.middlewarePath = middlewarePath;
420
+ this.fallbackPath = fallbackPath;
387
421
  }
388
- async handleMiddleware(req, reply, route, request_data, callback) {
389
- let components = await this.manager.getLayoutComponents(route);
390
- let middleware_stack = await Promise.all(components.map(async (component) => {
391
- const middleware = [...component.middleware || [], ...component.middlewares || []];
392
- return middleware;
393
- }));
394
- const layout_middlewares = middleware_stack.flat() || [];
395
- console.log("middleware stack loaded", layout_middlewares);
396
- req.params = request_data.params;
397
- await new Promise((resolve) => {
398
- this.runMiddleware(layout_middlewares, req, reply, resolve);
399
- });
400
- console.log("layout middleware executed");
401
- console.log("view middleware loaded");
402
- console.log("view middleware", route.view);
403
- let view = {};
404
- if (route.view) {
405
- view = await this.manager.getViewComponent(route);
406
- }
407
- let view_middlewares = [...view.middleware || [], ...view.middlewares || []];
408
- await new Promise((resolve) => {
409
- this.runMiddleware(view_middlewares, req, reply, resolve);
410
- });
411
- callback();
422
+ static build(middlewarePath, fallbackPath) {
423
+ return new MiddlewareAdapter(middlewarePath, fallbackPath);
412
424
  }
413
- runMiddleware(middleware, req, reply, callback) {
414
- middleware = this.dedupe(middleware);
415
- let index = 0;
416
- if (middleware.length === 0) {
417
- callback();
425
+ async handleMiddleware(req, res, middlewareConfig = []) {
426
+ if (!Array.isArray(middlewareConfig) || middlewareConfig.length === 0) {
418
427
  return;
419
428
  }
420
- let next = () => {
421
- if (index >= middleware.length) {
422
- callback();
423
- return;
429
+ const resolution = await this.resolveMiddlewareFunctions(middlewareConfig);
430
+ console.log(resolution);
431
+ if (resolution.errors.length > 0) {
432
+ throw new Error(`Middleware resolution failed: ${resolution.errors.join(", ")}`);
433
+ }
434
+ for (const middleware of resolution.functions) {
435
+ await this.executeMiddleware(middleware, req, res);
436
+ if (res.headersSent) {
437
+ break;
424
438
  }
425
- const mw = middleware[index];
426
- index++;
427
- if (typeof mw === "function") {
428
- if (!reply.sent)
429
- mw(req, reply, next);
430
- } else {
431
- if (!reply.sent)
432
- next();
439
+ }
440
+ }
441
+ async resolveMiddlewareFunctions(middlewareConfig) {
442
+ const functions = [];
443
+ const errors = [];
444
+ for (const spec of middlewareConfig) {
445
+ if (!spec) {
446
+ continue;
433
447
  }
448
+ try {
449
+ let resolved = this.cache.get(spec);
450
+ if (!resolved) {
451
+ resolved = await this.resolveMiddlewareSpec(spec);
452
+ this.cache.set(spec, [...resolved]);
453
+ }
454
+ functions.push(...resolved);
455
+ } catch (error) {
456
+ errors.push(`${spec}: ${error.message}`);
457
+ }
458
+ }
459
+ return {
460
+ functions: this.dedupe(functions),
461
+ errors
434
462
  };
435
- next();
436
463
  }
437
- dedupe(middlewares) {
438
- return Array.from(new Set(middlewares));
464
+ async resolveMiddlewareSpec(spec) {
465
+ const [filePath, selector] = this.parseMiddlewareSpec(spec);
466
+ const fullPath = this.resolveMiddlewareFilePath(filePath);
467
+ const module = await this.importMiddlewareModule(fullPath, spec);
468
+ return this.extractMiddlewareFunctions(module, selector, spec);
469
+ }
470
+ parseMiddlewareSpec(spec) {
471
+ const colonIndex = spec.indexOf(":");
472
+ if (colonIndex === -1) {
473
+ return [spec, void 0];
474
+ }
475
+ return [spec.substring(0, colonIndex), spec.substring(colonIndex + 1)];
476
+ }
477
+ resolveMiddlewareFilePath(filePath) {
478
+ const basePath = this.middlewarePath || this.fallbackPath || process.cwd();
479
+ const transformed = this.middlewareFileTransform(filePath);
480
+ const baseCandidate = path.resolve(process.cwd(), path.join(basePath, transformed));
481
+ const resolvedWithExt = this.findExistingFile(baseCandidate);
482
+ if (resolvedWithExt) {
483
+ return resolvedWithExt;
484
+ }
485
+ throw new Error(`Middleware file not found: ${transformed}`);
486
+ }
487
+ middlewareFileTransform(filePath) {
488
+ return filePath.split(".").join(path.sep);
489
+ }
490
+ findExistingFile(basePathWithoutExt) {
491
+ const ext = path.extname(basePathWithoutExt);
492
+ if (ext) {
493
+ return fs.existsSync(basePathWithoutExt) ? basePathWithoutExt : null;
494
+ }
495
+ const directCandidates = EXTENSION_CANDIDATES.map((extCandidate) => `${basePathWithoutExt}${extCandidate}`);
496
+ for (const candidate of directCandidates) {
497
+ if (fs.existsSync(candidate)) {
498
+ return candidate;
499
+ }
500
+ }
501
+ const indexCandidates = EXTENSION_CANDIDATES.map((extCandidate) => path.join(basePathWithoutExt, `index${extCandidate}`));
502
+ for (const candidate of indexCandidates) {
503
+ if (fs.existsSync(candidate)) {
504
+ return candidate;
505
+ }
506
+ }
507
+ return null;
508
+ }
509
+ async importMiddlewareModule(fullPath, spec) {
510
+ try {
511
+ const url = pathToFileURL(fullPath).href;
512
+ return await import(url);
513
+ } catch (error) {
514
+ throw new Error(`Unable to import middleware '${spec}' from ${fullPath}`);
515
+ }
516
+ }
517
+ extractMiddlewareFunctions(module, selector, spec) {
518
+ if (!selector) {
519
+ return this.extractDefaultFunction(module, spec);
520
+ }
521
+ if (selector === "*") {
522
+ return this.extractAllFunctions(module, spec);
523
+ }
524
+ return this.extractSpecificFunctions(module, selector, spec);
525
+ }
526
+ extractDefaultFunction(module, spec) {
527
+ if (!(module == null ? void 0 : module.default)) {
528
+ throw new Error(`Middleware '${spec}' is missing a default export`);
529
+ }
530
+ if (typeof module.default !== "function") {
531
+ throw new Error(`Default export for middleware '${spec}' is not a function`);
532
+ }
533
+ return [module.default];
534
+ }
535
+ extractAllFunctions(module, spec) {
536
+ const functions = Object.values(module).filter((value) => typeof value === "function");
537
+ if (functions.length === 0) {
538
+ throw new Error(`No functions exported by middleware '${spec}'`);
539
+ }
540
+ return functions;
541
+ }
542
+ extractSpecificFunctions(module, selector, spec) {
543
+ const names = selector.split(",").map((name) => name.trim()).filter(Boolean);
544
+ const functions = [];
545
+ const missing = [];
546
+ const invalid = [];
547
+ for (const name of names) {
548
+ const value = name === "default" ? module == null ? void 0 : module.default : module == null ? void 0 : module[name];
549
+ if (value === void 0) {
550
+ missing.push(name);
551
+ continue;
552
+ }
553
+ if (typeof value !== "function") {
554
+ invalid.push(name);
555
+ continue;
556
+ }
557
+ functions.push(value);
558
+ }
559
+ if (missing.length) {
560
+ throw new Error(`Middleware '${spec}' is missing export(s): ${missing.join(", ")}`);
561
+ }
562
+ if (invalid.length) {
563
+ throw new Error(`Middleware '${spec}' export(s) not functions: ${invalid.join(", ")}`);
564
+ }
565
+ if (functions.length === 0) {
566
+ throw new Error(`Middleware '${spec}' did not resolve any functions for selector '${selector}'`);
567
+ }
568
+ return functions;
569
+ }
570
+ executeMiddleware(fn, req, res) {
571
+ return new Promise((resolve, reject) => {
572
+ try {
573
+ fn(req, res, (error) => {
574
+ if (error) {
575
+ reject(error);
576
+ return;
577
+ }
578
+ resolve();
579
+ });
580
+ } catch (error) {
581
+ reject(error);
582
+ }
583
+ });
584
+ }
585
+ dedupe(functions) {
586
+ const seen = /* @__PURE__ */ new Set();
587
+ const ordered = [];
588
+ for (const fn of functions) {
589
+ if (!seen.has(fn)) {
590
+ seen.add(fn);
591
+ ordered.push(fn);
592
+ }
593
+ }
594
+ return ordered;
439
595
  }
440
596
  }
441
597
  function makeUrlParser(pattern) {
@@ -455,13 +611,14 @@ function initialize_route_matchers(routes) {
455
611
  class ServerAdapter {
456
612
  }
457
613
  class ExpressServerAdapter extends ServerAdapter {
458
- constructor(server, manager, htmlRender, api_adapter, middleware_adapter) {
614
+ constructor(server, manager, htmlRender, api_adapter, middleware_adapter, context_builder) {
459
615
  super();
460
616
  this.server = server;
461
617
  this.manager = manager;
462
618
  this.htmlRender = htmlRender;
463
619
  this.api_adapter = api_adapter;
464
620
  this.middleware_adapter = middleware_adapter;
621
+ this.context_builder = context_builder;
465
622
  }
466
623
  async handleRoutes(routes, manifest) {
467
624
  const matchers = initialize_route_matchers(routes);
@@ -501,7 +658,14 @@ class ExpressServerAdapter extends ServerAdapter {
501
658
  var _a, _b;
502
659
  let accepts = req.headers.accept || "";
503
660
  let server_api_call = accepts.toLowerCase().includes("application/json");
661
+ if (res.headersSent) {
662
+ return;
663
+ }
504
664
  const context = {};
665
+ if (this.context_builder) {
666
+ const built_context = await this.context_builder(req, res);
667
+ Object.assign(context, built_context);
668
+ }
505
669
  req["context"] = context;
506
670
  const cleanPath = url.split("?")[0];
507
671
  const pathParams = matcher.parser(cleanPath);
@@ -525,20 +689,17 @@ class ExpressServerAdapter extends ServerAdapter {
525
689
  context
526
690
  };
527
691
  try {
528
- await new Promise((resolve, reject) => {
529
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve).catch(reject);
530
- });
692
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
531
693
  } catch (e) {
532
694
  console.log("Error in middleware", e);
533
695
  res.status(500).send(`
534
696
  <h1>500 Internal Server Error</h1>
535
697
  <pre>${e}</pre>
698
+ <pre>Stack Trace:
699
+ ${e.stack}</pre>
536
700
  `);
537
701
  return;
538
702
  }
539
- if (res.headersSent) {
540
- return;
541
- }
542
703
  let server_data = await this.api_adapter.getApiData(route, request_data);
543
704
  if (server_api_call) {
544
705
  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);
@@ -554,6 +715,8 @@ class ExpressServerAdapter extends ServerAdapter {
554
715
  res.status(500).send(`
555
716
  <h1>500 Internal Server Error</h1>
556
717
  <pre>${e}</pre>
718
+ <pre>Stack Trace:
719
+ ${e.stack}</pre>
557
720
  `);
558
721
  return;
559
722
  }
@@ -628,9 +791,16 @@ view: ${route.view}</pre>`;
628
791
  context
629
792
  };
630
793
  let request_data = fallback_data;
631
- await new Promise((resolve) => {
632
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve);
633
- });
794
+ try {
795
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
796
+ } catch (e) {
797
+ console.log("Error in fallback middleware", e);
798
+ res.status(500).send(`
799
+ <h1>500 Internal Server Error</h1>
800
+ <pre>${e}</pre>
801
+ `);
802
+ return;
803
+ }
634
804
  console.log("Completed fallback middleware");
635
805
  let server_data = await this.api_adapter.getApiData(route, request_data);
636
806
  console.log("Completed fallback API call");
@@ -704,13 +874,18 @@ ${JSON.stringify(options, null, 2)}`);
704
874
  const routeDefs = server_config.routes;
705
875
  const manager = new ComponentManager(componentLoader);
706
876
  const api_adapter = new ApiAdapter(manager);
707
- const middleware_adapter = new MiddlewareAdapter(manager);
877
+ let middlewareRoot;
878
+ if (full_options.middleware_path) {
879
+ middlewareRoot = path.isAbsolute(full_options.middleware_path) ? full_options.middleware_path : path.resolve(full_options.viteOptions.root, full_options.middleware_path);
880
+ }
881
+ const middleware_adapter = MiddlewareAdapter.build(middlewareRoot, COMPONENT_DIR);
708
882
  const adapter = new ExpressServerAdapter(
709
883
  app,
710
884
  manager,
711
885
  htmlRenderer,
712
886
  api_adapter,
713
- middleware_adapter
887
+ middleware_adapter,
888
+ full_options.context_builder
714
889
  );
715
890
  await adapter.handleRoutes(routeDefs, manifest);
716
891
  console.log("Routes registered");