@marko/run 0.11.0-rc.1 → 0.11.0-rc.3

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.
@@ -22,6 +22,7 @@ var internal_exports = {};
22
22
  __export(internal_exports, {
23
23
  NotHandled: () => NotHandled,
24
24
  NotMatched: () => NotMatched,
25
+ assertHandlerVerb: () => assertHandlerVerb,
25
26
  call: () => call,
26
27
  compose: () => compose,
27
28
  createContext: () => createContext,
@@ -58,6 +59,28 @@ var httpVerbs = [
58
59
  "options"
59
60
  ];
60
61
 
62
+ // src/runtime/thenable.ts
63
+ var kPromise = /* @__PURE__ */ Symbol("promise");
64
+ var kReadFn = /* @__PURE__ */ Symbol("read fn");
65
+ function thenFn(resolve, reject) {
66
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).then(resolve, reject);
67
+ }
68
+ function catchFn(reject) {
69
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).catch(reject);
70
+ }
71
+ function finallyFn(resolve) {
72
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).finally(resolve);
73
+ }
74
+ function thenable(fn) {
75
+ return {
76
+ [kPromise]: null,
77
+ [kReadFn]: fn,
78
+ then: thenFn,
79
+ catch: catchFn,
80
+ finally: finallyFn
81
+ };
82
+ }
83
+
61
84
  // src/runtime/url-builder.ts
62
85
  var encode = encodeURIComponent;
63
86
  var pathParts = /* @__PURE__ */ new Map();
@@ -162,18 +185,16 @@ globalThis.MarkoRun ?? (globalThis.MarkoRun = {
162
185
  NotHandled,
163
186
  NotMatched
164
187
  });
165
- if (!globalThis.Run) {
166
- const namespace = {
167
- href
168
- };
169
- for (const v of [...httpVerbs, "all"]) {
170
- const verb = v.toUpperCase();
171
- const def = createDefineHandler();
172
- def.href = href;
173
- namespace[verb] = def;
174
- }
175
- globalThis.Run = namespace;
176
- }
188
+ globalThis.Run ?? (globalThis.Run = {
189
+ href,
190
+ ALL: createDefineHandler("ALL"),
191
+ ...Object.fromEntries(
192
+ httpVerbs.map((v) => {
193
+ const verb = v.toUpperCase();
194
+ return [v.toUpperCase(), createDefineHandler(verb)];
195
+ })
196
+ )
197
+ });
177
198
  var toReadable = (rendered) => {
178
199
  toReadable = rendered.toReadable ? (rendered2) => rendered2.toReadable() : (rendered2) => {
179
200
  let cancelled = false;
@@ -241,6 +262,29 @@ async function readBodyWithLimit(request, maxBytes) {
241
262
  bytes.subarray(0, receivedBytes)
242
263
  );
243
264
  }
265
+ async function readBody(route, context) {
266
+ const { request } = context;
267
+ const contentType = request.headers.get("Content-Type");
268
+ if (contentType == null ? void 0 : contentType.includes("application/json")) {
269
+ const { maxBytes: maxBytes2, validator: validator2 } = route.options.json;
270
+ const json = maxBytes2 < 0 ? await request.json() : JSON.parse(await readBodyWithLimit(request, maxBytes2));
271
+ return validator2 ? validator2(json) : json;
272
+ }
273
+ const { maxBytes, maxParts, maxFiles, maxFileBytes, onFile, validator } = route.options.form;
274
+ const data = searchParamsToObject(
275
+ (contentType == null ? void 0 : contentType.includes("multipart/form-data")) ? await (0, import_form_data_parser.parseFormData)(
276
+ request,
277
+ {
278
+ maxParts,
279
+ maxFiles,
280
+ maxFileSize: maxFileBytes,
281
+ maxTotalSize: maxBytes
282
+ },
283
+ onFile ? (file) => onFile(context, file) : void 0
284
+ ) : new import_node_url.URLSearchParams(await readBodyWithLimit(request, maxBytes))
285
+ );
286
+ return validator && validator(data);
287
+ }
244
288
  function createContext(route, request, platform, url = new URL(request.url)) {
245
289
  const context = {
246
290
  route: (route == null ? void 0 : route.path) || "",
@@ -265,45 +309,7 @@ function createContext(route, request, platform, url = new URL(request.url)) {
265
309
  });
266
310
  return value;
267
311
  },
268
- body: route && request.body ? async () => {
269
- const contentType = request.headers.get("Content-Type");
270
- let value;
271
- if (contentType == null ? void 0 : contentType.includes("application/json")) {
272
- const { maxBytes, validator } = route.options.json;
273
- const json = maxBytes < 0 ? await request.json() : JSON.parse(await readBodyWithLimit(request, maxBytes));
274
- value = validator ? validator(json) : json;
275
- } else {
276
- const {
277
- maxBytes,
278
- maxParts,
279
- maxFiles,
280
- maxFileBytes,
281
- onFile,
282
- validator
283
- } = route.options.form;
284
- const data = searchParamsToObject(
285
- (contentType == null ? void 0 : contentType.includes("multipart/form-data")) ? await (0, import_form_data_parser.parseFormData)(
286
- request,
287
- {
288
- maxParts,
289
- maxFiles,
290
- maxFileSize: maxFileBytes,
291
- maxTotalSize: maxBytes
292
- },
293
- onFile ? (file) => onFile(context, file) : void 0
294
- ) : new import_node_url.URLSearchParams(
295
- await readBodyWithLimit(request, maxBytes)
296
- )
297
- );
298
- value = validator ? validator(data) : validator;
299
- }
300
- Object.defineProperty(context, "body", {
301
- configurable: true,
302
- enumerable: true,
303
- value
304
- });
305
- return value;
306
- } : void 0,
312
+ body: route && request.body ? thenable(() => readBody(route, context)) : void 0,
307
313
  data: {},
308
314
  url,
309
315
  request,
@@ -357,11 +363,22 @@ function render(context, template, input, data) {
357
363
  }
358
364
  return context.render(template, input);
359
365
  }
366
+ var handlerMethod = /* @__PURE__ */ new WeakMap();
360
367
  async function call(handler, next, context, data) {
361
368
  let response;
362
369
  if (data) {
363
370
  Object.assign(context.data, data);
364
371
  }
372
+ let method = handlerMethod.get(handler);
373
+ if (method === void 0) {
374
+ handlerMethod.set(
375
+ handler,
376
+ method = "verb" in handler && handler.verb !== "ALL" ? handler.verb : false
377
+ );
378
+ }
379
+ if (method && method !== context.method) {
380
+ return next(data);
381
+ }
365
382
  if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
366
383
  let nextCallCount = 0;
367
384
  let didThrow = false;
@@ -402,7 +419,7 @@ async function call(handler, next, context, data) {
402
419
  if (response === null || response === NotMatched || response === NotHandled) {
403
420
  throw response || NotMatched;
404
421
  }
405
- return response || next();
422
+ return response || next(data);
406
423
  }
407
424
  function compose(handlers) {
408
425
  const len = handlers.length;
@@ -435,25 +452,37 @@ function normalizeHandler(obj) {
435
452
  }
436
453
  return passthrough;
437
454
  }
438
- function createDefineHandler() {
455
+ function assertHandlerVerb(verb, handler) {
456
+ if ("verb" in handler && handler.verb !== verb) {
457
+ throw new Error(
458
+ `Expected verb ${verb} but handler was defined with Run.${handler.verb}`
459
+ );
460
+ }
461
+ }
462
+ function createDefineHandler(verb) {
439
463
  return (optionsOrHandlers, handlers) => {
440
464
  let handler;
441
465
  if (typeof optionsOrHandlers === "function") {
466
+ assertHandlerVerb(verb, optionsOrHandlers);
442
467
  handler = optionsOrHandlers;
443
- handler.options = {};
468
+ handler.options ?? (handler.options = {});
444
469
  } else if (Array.isArray(optionsOrHandlers)) {
470
+ for (const h of optionsOrHandlers) assertHandlerVerb(verb, h);
445
471
  handler = compose(optionsOrHandlers);
446
- handler.options = {};
472
+ handler.options = mergeOptions(...optionsOrHandlers);
447
473
  } else if (typeof handlers === "function") {
474
+ assertHandlerVerb(verb, handlers);
448
475
  handler = handlers;
449
- handler.options = optionsOrHandlers;
476
+ handler.options = mergeOptions(handlers, optionsOrHandlers);
450
477
  } else if (Array.isArray(handlers)) {
478
+ for (const h of handlers) assertHandlerVerb(verb, h);
451
479
  handler = compose(handlers);
452
- handler.options = optionsOrHandlers;
480
+ handler.options = mergeOptions(...handlers, optionsOrHandlers);
453
481
  } else {
454
482
  handler = passthroughHandler;
455
483
  handler.options = optionsOrHandlers;
456
484
  }
485
+ handler.verb = verb;
457
486
  return handler;
458
487
  };
459
488
  }
@@ -469,19 +498,24 @@ function normalizeValidator(validator) {
469
498
  var defaultMaxBytes = 1024 * 1024;
470
499
  var defaultMaxParts = 1e3;
471
500
  var defaultMaxFiles = 20;
472
- function mergeOptions(...fns) {
501
+ function mergeOptions(...arr) {
473
502
  const merged = {};
474
- for (const fn of fns) {
475
- if (typeof fn === "function" && "options" in fn) {
476
- const { options } = fn;
477
- for (const k in options) {
478
- const key = k;
479
- const option = options[key];
480
- if (typeof option === "object" && typeof merged[key] === "object") {
481
- Object.assign(merged[key], option);
482
- } else if (option) {
483
- merged[key] = option;
484
- }
503
+ for (const item of arr) {
504
+ let options;
505
+ if (typeof item === "object") {
506
+ options = item;
507
+ } else if ("options" in item) {
508
+ options = item.options;
509
+ } else {
510
+ continue;
511
+ }
512
+ for (const k in options) {
513
+ const key = k;
514
+ const option = options[key];
515
+ if (typeof option === "object" && typeof merged[key] === "object") {
516
+ Object.assign(merged[key], option);
517
+ } else if (option) {
518
+ merged[key] = option;
485
519
  }
486
520
  }
487
521
  }
@@ -540,6 +574,7 @@ function notMatched() {
540
574
  0 && (module.exports = {
541
575
  NotHandled,
542
576
  NotMatched,
577
+ assertHandlerVerb,
543
578
  call,
544
579
  compose,
545
580
  createContext,
@@ -1,5 +1,5 @@
1
1
  import type { Awaitable, RouteHandler } from "./legacy-types";
2
- import type { Context, HandlerFunction, HandlerOptions, NextFunction, NormalizedHandler, NormalizedHandlerOptions, Platform, RouteData, RouteMatch, Validator } from "./types";
2
+ import type { Context, HandlerFunction, HandlerOptions, HttpVerbOrAll, NextFunction, NormalizedHandler, NormalizedHandlerOptions, Platform, RouteData, RouteMatch, Validator } from "./types";
3
3
  export { getMetaDataLookup as normalizeMeta } from "../vite/utils/meta-data";
4
4
  export declare const NotHandled: typeof MarkoRun.NotHandled;
5
5
  export declare const NotMatched: typeof MarkoRun.NotMatched;
@@ -8,8 +8,9 @@ export declare function render<T>(context: Context, template: Marko.Template<T>,
8
8
  export declare function call(handler: HandlerFunction, next: NextFunction, context: Context, data?: RouteData): Promise<Response>;
9
9
  export declare function compose(handlers: HandlerFunction[]): HandlerFunction;
10
10
  export declare function normalizeHandler(obj: RouteHandler | RouteHandler[] | Promise<RouteHandler | RouteHandler[]>): RouteHandler;
11
+ export declare function assertHandlerVerb(verb: HttpVerbOrAll, handler: HandlerFunction): void;
11
12
  export declare function normalizeValidator<T>(validator: Validator<T> | undefined): import("./types").ValidatorFn<T> | undefined;
12
- export declare function mergeOptions(...fns: (NormalizedHandler<Context, "ALL", any, HandlerOptions> | HandlerFunction)[]): NormalizedHandlerOptions;
13
+ export declare function mergeOptions(...arr: (NormalizedHandler<Context, "ALL", any, HandlerOptions> | HandlerFunction | HandlerOptions)[]): NormalizedHandlerOptions;
13
14
  export declare function stripResponseBodySync(response: Response): Response;
14
15
  export declare function stripResponseBody(response: Awaitable<Response>): Awaitable<Response>;
15
16
  export declare function passthrough(): void;
@@ -13,6 +13,28 @@ var httpVerbs = [
13
13
  "options"
14
14
  ];
15
15
 
16
+ // src/runtime/thenable.ts
17
+ var kPromise = /* @__PURE__ */ Symbol("promise");
18
+ var kReadFn = /* @__PURE__ */ Symbol("read fn");
19
+ function thenFn(resolve, reject) {
20
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).then(resolve, reject);
21
+ }
22
+ function catchFn(reject) {
23
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).catch(reject);
24
+ }
25
+ function finallyFn(resolve) {
26
+ return (this[kPromise] || (this[kPromise] = this[kReadFn]())).finally(resolve);
27
+ }
28
+ function thenable(fn) {
29
+ return {
30
+ [kPromise]: null,
31
+ [kReadFn]: fn,
32
+ then: thenFn,
33
+ catch: catchFn,
34
+ finally: finallyFn
35
+ };
36
+ }
37
+
16
38
  // src/runtime/url-builder.ts
17
39
  var encode = encodeURIComponent;
18
40
  var pathParts = /* @__PURE__ */ new Map();
@@ -117,18 +139,16 @@ globalThis.MarkoRun ?? (globalThis.MarkoRun = {
117
139
  NotHandled,
118
140
  NotMatched
119
141
  });
120
- if (!globalThis.Run) {
121
- const namespace = {
122
- href
123
- };
124
- for (const v of [...httpVerbs, "all"]) {
125
- const verb = v.toUpperCase();
126
- const def = createDefineHandler();
127
- def.href = href;
128
- namespace[verb] = def;
129
- }
130
- globalThis.Run = namespace;
131
- }
142
+ globalThis.Run ?? (globalThis.Run = {
143
+ href,
144
+ ALL: createDefineHandler("ALL"),
145
+ ...Object.fromEntries(
146
+ httpVerbs.map((v) => {
147
+ const verb = v.toUpperCase();
148
+ return [v.toUpperCase(), createDefineHandler(verb)];
149
+ })
150
+ )
151
+ });
132
152
  var toReadable = (rendered) => {
133
153
  toReadable = rendered.toReadable ? (rendered2) => rendered2.toReadable() : (rendered2) => {
134
154
  let cancelled = false;
@@ -196,6 +216,29 @@ async function readBodyWithLimit(request, maxBytes) {
196
216
  bytes.subarray(0, receivedBytes)
197
217
  );
198
218
  }
219
+ async function readBody(route, context) {
220
+ const { request } = context;
221
+ const contentType = request.headers.get("Content-Type");
222
+ if (contentType == null ? void 0 : contentType.includes("application/json")) {
223
+ const { maxBytes: maxBytes2, validator: validator2 } = route.options.json;
224
+ const json = maxBytes2 < 0 ? await request.json() : JSON.parse(await readBodyWithLimit(request, maxBytes2));
225
+ return validator2 ? validator2(json) : json;
226
+ }
227
+ const { maxBytes, maxParts, maxFiles, maxFileBytes, onFile, validator } = route.options.form;
228
+ const data = searchParamsToObject(
229
+ (contentType == null ? void 0 : contentType.includes("multipart/form-data")) ? await parseFormData(
230
+ request,
231
+ {
232
+ maxParts,
233
+ maxFiles,
234
+ maxFileSize: maxFileBytes,
235
+ maxTotalSize: maxBytes
236
+ },
237
+ onFile ? (file) => onFile(context, file) : void 0
238
+ ) : new URLSearchParams2(await readBodyWithLimit(request, maxBytes))
239
+ );
240
+ return validator && validator(data);
241
+ }
199
242
  function createContext(route, request, platform, url = new URL(request.url)) {
200
243
  const context = {
201
244
  route: (route == null ? void 0 : route.path) || "",
@@ -220,45 +263,7 @@ function createContext(route, request, platform, url = new URL(request.url)) {
220
263
  });
221
264
  return value;
222
265
  },
223
- body: route && request.body ? async () => {
224
- const contentType = request.headers.get("Content-Type");
225
- let value;
226
- if (contentType == null ? void 0 : contentType.includes("application/json")) {
227
- const { maxBytes, validator } = route.options.json;
228
- const json = maxBytes < 0 ? await request.json() : JSON.parse(await readBodyWithLimit(request, maxBytes));
229
- value = validator ? validator(json) : json;
230
- } else {
231
- const {
232
- maxBytes,
233
- maxParts,
234
- maxFiles,
235
- maxFileBytes,
236
- onFile,
237
- validator
238
- } = route.options.form;
239
- const data = searchParamsToObject(
240
- (contentType == null ? void 0 : contentType.includes("multipart/form-data")) ? await parseFormData(
241
- request,
242
- {
243
- maxParts,
244
- maxFiles,
245
- maxFileSize: maxFileBytes,
246
- maxTotalSize: maxBytes
247
- },
248
- onFile ? (file) => onFile(context, file) : void 0
249
- ) : new URLSearchParams2(
250
- await readBodyWithLimit(request, maxBytes)
251
- )
252
- );
253
- value = validator ? validator(data) : validator;
254
- }
255
- Object.defineProperty(context, "body", {
256
- configurable: true,
257
- enumerable: true,
258
- value
259
- });
260
- return value;
261
- } : void 0,
266
+ body: route && request.body ? thenable(() => readBody(route, context)) : void 0,
262
267
  data: {},
263
268
  url,
264
269
  request,
@@ -312,11 +317,22 @@ function render(context, template, input, data) {
312
317
  }
313
318
  return context.render(template, input);
314
319
  }
320
+ var handlerMethod = /* @__PURE__ */ new WeakMap();
315
321
  async function call(handler, next, context, data) {
316
322
  let response;
317
323
  if (data) {
318
324
  Object.assign(context.data, data);
319
325
  }
326
+ let method = handlerMethod.get(handler);
327
+ if (method === void 0) {
328
+ handlerMethod.set(
329
+ handler,
330
+ method = "verb" in handler && handler.verb !== "ALL" ? handler.verb : false
331
+ );
332
+ }
333
+ if (method && method !== context.method) {
334
+ return next(data);
335
+ }
320
336
  if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
321
337
  let nextCallCount = 0;
322
338
  let didThrow = false;
@@ -357,7 +373,7 @@ async function call(handler, next, context, data) {
357
373
  if (response === null || response === NotMatched || response === NotHandled) {
358
374
  throw response || NotMatched;
359
375
  }
360
- return response || next();
376
+ return response || next(data);
361
377
  }
362
378
  function compose(handlers) {
363
379
  const len = handlers.length;
@@ -390,25 +406,37 @@ function normalizeHandler(obj) {
390
406
  }
391
407
  return passthrough;
392
408
  }
393
- function createDefineHandler() {
409
+ function assertHandlerVerb(verb, handler) {
410
+ if ("verb" in handler && handler.verb !== verb) {
411
+ throw new Error(
412
+ `Expected verb ${verb} but handler was defined with Run.${handler.verb}`
413
+ );
414
+ }
415
+ }
416
+ function createDefineHandler(verb) {
394
417
  return (optionsOrHandlers, handlers) => {
395
418
  let handler;
396
419
  if (typeof optionsOrHandlers === "function") {
420
+ assertHandlerVerb(verb, optionsOrHandlers);
397
421
  handler = optionsOrHandlers;
398
- handler.options = {};
422
+ handler.options ?? (handler.options = {});
399
423
  } else if (Array.isArray(optionsOrHandlers)) {
424
+ for (const h of optionsOrHandlers) assertHandlerVerb(verb, h);
400
425
  handler = compose(optionsOrHandlers);
401
- handler.options = {};
426
+ handler.options = mergeOptions(...optionsOrHandlers);
402
427
  } else if (typeof handlers === "function") {
428
+ assertHandlerVerb(verb, handlers);
403
429
  handler = handlers;
404
- handler.options = optionsOrHandlers;
430
+ handler.options = mergeOptions(handlers, optionsOrHandlers);
405
431
  } else if (Array.isArray(handlers)) {
432
+ for (const h of handlers) assertHandlerVerb(verb, h);
406
433
  handler = compose(handlers);
407
- handler.options = optionsOrHandlers;
434
+ handler.options = mergeOptions(...handlers, optionsOrHandlers);
408
435
  } else {
409
436
  handler = passthroughHandler;
410
437
  handler.options = optionsOrHandlers;
411
438
  }
439
+ handler.verb = verb;
412
440
  return handler;
413
441
  };
414
442
  }
@@ -424,19 +452,24 @@ function normalizeValidator(validator) {
424
452
  var defaultMaxBytes = 1024 * 1024;
425
453
  var defaultMaxParts = 1e3;
426
454
  var defaultMaxFiles = 20;
427
- function mergeOptions(...fns) {
455
+ function mergeOptions(...arr) {
428
456
  const merged = {};
429
- for (const fn of fns) {
430
- if (typeof fn === "function" && "options" in fn) {
431
- const { options } = fn;
432
- for (const k in options) {
433
- const key = k;
434
- const option = options[key];
435
- if (typeof option === "object" && typeof merged[key] === "object") {
436
- Object.assign(merged[key], option);
437
- } else if (option) {
438
- merged[key] = option;
439
- }
457
+ for (const item of arr) {
458
+ let options;
459
+ if (typeof item === "object") {
460
+ options = item;
461
+ } else if ("options" in item) {
462
+ options = item.options;
463
+ } else {
464
+ continue;
465
+ }
466
+ for (const k in options) {
467
+ const key = k;
468
+ const option = options[key];
469
+ if (typeof option === "object" && typeof merged[key] === "object") {
470
+ Object.assign(merged[key], option);
471
+ } else if (option) {
472
+ merged[key] = option;
440
473
  }
441
474
  }
442
475
  }
@@ -494,6 +527,7 @@ function notMatched() {
494
527
  export {
495
528
  NotHandled,
496
529
  NotMatched,
530
+ assertHandlerVerb,
497
531
  call,
498
532
  compose,
499
533
  createContext,
@@ -0,0 +1,11 @@
1
+ declare const kPromise: unique symbol;
2
+ declare const kReadFn: unique symbol;
3
+ export interface Thenable<T = any> extends Promise<T> {
4
+ [kPromise]: null | Promise<T>;
5
+ [kReadFn]: () => Promise<T>;
6
+ then: Promise<T>["then"];
7
+ catch: Promise<T>["catch"];
8
+ finally: Promise<T>["finally"];
9
+ }
10
+ export default function thenable<T>(fn: () => Promise<T>): Thenable<T>;
11
+ export {};