@noego/forge 0.0.7 → 0.0.8

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 +243 -71
  28. package/dist-ssr/server.cjs.map +1 -1
  29. package/dist-ssr/server.d.ts +6 -1
  30. package/dist-ssr/server.js +238 -66
  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,18 @@ 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
+ console.log(JSON.stringify(route, null, 2));
527
685
  const context = {};
686
+ if (this.context_builder) {
687
+ const built_context = await this.context_builder(req, res);
688
+ Object.assign(context, built_context);
689
+ }
528
690
  req["context"] = context;
529
- const cleanPath = url.split("?")[0];
691
+ const cleanPath = url2.split("?")[0];
530
692
  const pathParams = matcher.parser(cleanPath);
531
693
  const queryParams = {};
532
694
  for (const [key, value] of Object.entries(req.query)) {
@@ -540,7 +702,7 @@ class ExpressServerAdapter extends ServerAdapter {
540
702
  }
541
703
  const combinedParams = { ...pathParams, ...queryParams };
542
704
  let request_data = {
543
- url,
705
+ url: url2,
544
706
  query: req.query,
545
707
  params: combinedParams,
546
708
  headers: req.headers,
@@ -548,9 +710,7 @@ class ExpressServerAdapter extends ServerAdapter {
548
710
  context
549
711
  };
550
712
  try {
551
- await new Promise((resolve, reject) => {
552
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve).catch(reject);
553
- });
713
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
554
714
  } catch (e) {
555
715
  console.log("Error in middleware", e);
556
716
  res.status(500).send(`
@@ -640,10 +800,10 @@ view: ${route.view}</pre>`;
640
800
  let route = server_config.fallback;
641
801
  let accepts = req.headers.accept || "";
642
802
  let server_api_call = accepts.toLowerCase().includes("application/json");
643
- const url = req.url || "/";
803
+ const url2 = req.url || "/";
644
804
  const context = {};
645
805
  let fallback_data = {
646
- url,
806
+ url: url2,
647
807
  query: req.query,
648
808
  params: {},
649
809
  headers: req.headers,
@@ -651,9 +811,16 @@ view: ${route.view}</pre>`;
651
811
  context
652
812
  };
653
813
  let request_data = fallback_data;
654
- await new Promise((resolve) => {
655
- this.middleware_adapter.handleMiddleware(req, res, route, request_data, resolve);
656
- });
814
+ try {
815
+ await this.middleware_adapter.handleMiddleware(req, res, route.middleware || []);
816
+ } catch (e) {
817
+ console.log("Error in fallback middleware", e);
818
+ res.status(500).send(`
819
+ <h1>500 Internal Server Error</h1>
820
+ <pre>${e}</pre>
821
+ `);
822
+ return;
823
+ }
657
824
  console.log("Completed fallback middleware");
658
825
  let server_data = await this.api_adapter.getApiData(route, request_data);
659
826
  console.log("Completed fallback API call");
@@ -727,13 +894,18 @@ ${JSON.stringify(options, null, 2)}`);
727
894
  const routeDefs = server_config.routes;
728
895
  const manager = new ComponentManager(componentLoader);
729
896
  const api_adapter = new ApiAdapter(manager);
730
- const middleware_adapter = new MiddlewareAdapter(manager);
897
+ let middlewareRoot;
898
+ if (full_options.middleware_path) {
899
+ middlewareRoot = path.isAbsolute(full_options.middleware_path) ? full_options.middleware_path : path.resolve(full_options.viteOptions.root, full_options.middleware_path);
900
+ }
901
+ const middleware_adapter = MiddlewareAdapter.build(middlewareRoot, COMPONENT_DIR);
731
902
  const adapter = new ExpressServerAdapter(
732
903
  app,
733
904
  manager,
734
905
  htmlRenderer,
735
906
  api_adapter,
736
- middleware_adapter
907
+ middleware_adapter,
908
+ full_options.context_builder
737
909
  );
738
910
  await adapter.handleRoutes(routeDefs, manifest);
739
911
  console.log("Routes registered");