git.iko.soy/purestack-dev/purescript-purestack-server
on master help sign in
index.jsdeleted−1633
since splitsnapshots
unifiedsplit
@@ -1,1633 +0,0 @@
1−(() => {
2− // output/Bun.Server/foreign.js
3− function serve(f) {
4− return function() {
5− return Bun.serve({
6− fetch(req) {
7− return f(req)();
8− }
9− });
10− };
11− }
12−
13− // output/Control.Apply/foreign.js
14− var arrayApply = function(fs) {
15− return function(xs) {
16− var l = fs.length;
17− var k = xs.length;
18− var result = new Array(l * k);
19− var n = 0;
20− for (var i = 0; i < l; i++) {
21− var f = fs[i];
22− for (var j = 0; j < k; j++) {
23− result[n++] = f(xs[j]);
24− }
25− }
26− return result;
27− };
28− };
29−
30− // output/Control.Semigroupoid/index.js
31− var semigroupoidFn = {
32− compose: function(f) {
33− return function(g) {
34− return function(x) {
35− return f(g(x));
36− };
37− };
38− }
39− };
40−
41− // output/Control.Category/index.js
42− var identity = function(dict) {
43− return dict.identity;
44− };
45− var categoryFn = {
46− identity: function(x) {
47− return x;
48− },
49− Semigroupoid0: function() {
50− return semigroupoidFn;
51− }
52− };
53−
54− // output/Data.Function/index.js
55− var flip = function(f) {
56− return function(b) {
57− return function(a) {
58− return f(a)(b);
59− };
60− };
61− };
62− var $$const = function(a) {
63− return function(v) {
64− return a;
65− };
66− };
67−
68− // output/Data.Functor/foreign.js
69− var arrayMap = function(f) {
70− return function(arr) {
71− var l = arr.length;
72− var result = new Array(l);
73− for (var i = 0; i < l; i++) {
74− result[i] = f(arr[i]);
75− }
76− return result;
77− };
78− };
79−
80− // output/Data.Unit/foreign.js
81− var unit = void 0;
82−
83− // output/Type.Proxy/index.js
84− var $$Proxy = /* @__PURE__ */ function() {
85− function $$Proxy2() {
86− }
87− ;
88− $$Proxy2.value = new $$Proxy2();
89− return $$Proxy2;
90− }();
91−
92− // output/Data.Functor/index.js
93− var map = function(dict) {
94− return dict.map;
95− };
96− var $$void = function(dictFunctor) {
97− return map(dictFunctor)($$const(unit));
98− };
99− var functorArray = {
100− map: arrayMap
101− };
102−
103− // output/Control.Apply/index.js
104− var applyArray = {
105− apply: arrayApply,
106− Functor0: function() {
107− return functorArray;
108− }
109− };
110− var apply = function(dict) {
111− return dict.apply;
112− };
113−
114− // output/Control.Applicative/index.js
115− var pure = function(dict) {
116− return dict.pure;
117− };
118− var liftA1 = function(dictApplicative) {
119− var apply3 = apply(dictApplicative.Apply0());
120− var pure13 = pure(dictApplicative);
121− return function(f) {
122− return function(a) {
123− return apply3(pure13(f))(a);
124− };
125− };
126− };
127−
128− // output/Data.Argonaut.Core/foreign.js
129− function id(x) {
130− return x;
131− }
132−
133− // output/Data.Eq/foreign.js
134− var refEq = function(r1) {
135− return function(r2) {
136− return r1 === r2;
137− };
138− };
139− var eqStringImpl = refEq;
140− var eqArrayImpl = function(f) {
141− return function(xs) {
142− return function(ys) {
143− if (xs.length !== ys.length) return false;
144− for (var i = 0; i < xs.length; i++) {
145− if (!f(xs[i])(ys[i])) return false;
146− }
147− return true;
148− };
149− };
150− };
151−
152− // output/Data.Symbol/index.js
153− var reflectSymbol = function(dict) {
154− return dict.reflectSymbol;
155− };
156−
157− // output/Record.Unsafe/foreign.js
158− var unsafeGet = function(label) {
159− return function(rec) {
160− return rec[label];
161− };
162− };
163− var unsafeDelete = function(label) {
164− return function(rec) {
165− var copy = {};
166− for (var key in rec) {
167− if (key !== label && {}.hasOwnProperty.call(rec, key)) {
168− copy[key] = rec[key];
169− }
170− }
171− return copy;
172− };
173− };
174−
175− // output/Data.Eq/index.js
176− var eqString = {
177− eq: eqStringImpl
178− };
179− var eq = function(dict) {
180− return dict.eq;
181− };
182− var eqArray = function(dictEq) {
183− return {
184− eq: eqArrayImpl(eq(dictEq))
185− };
186− };
187−
188− // output/Data.Semigroup/foreign.js
189− var concatArray = function(xs) {
190− return function(ys) {
191− if (xs.length === 0) return ys;
192− if (ys.length === 0) return xs;
193− return xs.concat(ys);
194− };
195− };
196−
197− // output/Data.Semigroup/index.js
198− var semigroupArray = {
199− append: concatArray
200− };
201− var append = function(dict) {
202− return dict.append;
203− };
204−
205− // output/Data.Bounded/foreign.js
206− var topChar = String.fromCharCode(65535);
207− var bottomChar = String.fromCharCode(0);
208− var topNumber = Number.POSITIVE_INFINITY;
209− var bottomNumber = Number.NEGATIVE_INFINITY;
210−
211− // output/Data.Maybe/index.js
212− var Nothing = /* @__PURE__ */ function() {
213− function Nothing2() {
214− }
215− ;
216− Nothing2.value = new Nothing2();
217− return Nothing2;
218− }();
219− var Just = /* @__PURE__ */ function() {
220− function Just2(value0) {
221− this.value0 = value0;
222− }
223− ;
224− Just2.create = function(value0) {
225− return new Just2(value0);
226− };
227− return Just2;
228− }();
229− var maybe = function(v) {
230− return function(v1) {
231− return function(v2) {
232− if (v2 instanceof Nothing) {
233− return v;
234− }
235− ;
236− if (v2 instanceof Just) {
237− return v1(v2.value0);
238− }
239− ;
240− throw new Error("Failed pattern match at Data.Maybe (line 237, column 1 - line 237, column 51): " + [v.constructor.name, v1.constructor.name, v2.constructor.name]);
241− };
242− };
243− };
244− var functorMaybe = {
245− map: function(v) {
246− return function(v1) {
247− if (v1 instanceof Just) {
248− return new Just(v(v1.value0));
249− }
250− ;
251− return Nothing.value;
252− };
253− }
254− };
255− var map2 = /* @__PURE__ */ map(functorMaybe);
256− var applyMaybe = {
257− apply: function(v) {
258− return function(v1) {
259− if (v instanceof Just) {
260− return map2(v.value0)(v1);
261− }
262− ;
263− if (v instanceof Nothing) {
264− return Nothing.value;
265− }
266− ;
267− throw new Error("Failed pattern match at Data.Maybe (line 67, column 1 - line 69, column 30): " + [v.constructor.name, v1.constructor.name]);
268− };
269− },
270− Functor0: function() {
271− return functorMaybe;
272− }
273− };
274− var bindMaybe = {
275− bind: function(v) {
276− return function(v1) {
277− if (v instanceof Just) {
278− return v1(v.value0);
279− }
280− ;
281− if (v instanceof Nothing) {
282− return Nothing.value;
283− }
284− ;
285− throw new Error("Failed pattern match at Data.Maybe (line 125, column 1 - line 127, column 28): " + [v.constructor.name, v1.constructor.name]);
286− };
287− },
288− Apply0: function() {
289− return applyMaybe;
290− }
291− };
292− var applicativeMaybe = /* @__PURE__ */ function() {
293− return {
294− pure: Just.create,
295− Apply0: function() {
296− return applyMaybe;
297− }
298− };
299− }();
300− var altMaybe = {
301− alt: function(v) {
302− return function(v1) {
303− if (v instanceof Nothing) {
304− return v1;
305− }
306− ;
307− return v;
308− };
309− },
310− Functor0: function() {
311− return functorMaybe;
312− }
313− };
314− var plusMaybe = /* @__PURE__ */ function() {
315− return {
316− empty: Nothing.value,
317− Alt0: function() {
318− return altMaybe;
319− }
320− };
321− }();
322−
323− // output/Foreign.Object/foreign.js
324− function _copyST(m) {
325− return function() {
326− var r = {};
327− for (var k in m) {
328− if (hasOwnProperty.call(m, k)) {
329− r[k] = m[k];
330− }
331− }
332− return r;
333− };
334− }
335− var empty = {};
336− function runST(f) {
337− return f();
338− }
339− function toArrayWithKey(f) {
340− return function(m) {
341− var r = [];
342− for (var k in m) {
343− if (hasOwnProperty.call(m, k)) {
344− r.push(f(k)(m[k]));
345− }
346− }
347− return r;
348− };
349− }
350− var keys = Object.keys || toArrayWithKey(function(k) {
351− return function() {
352− return k;
353− };
354− });
355−
356− // output/Control.Bind/foreign.js
357− var arrayBind = typeof Array.prototype.flatMap === "function" ? function(arr) {
358− return function(f) {
359− return arr.flatMap(f);
360− };
361− } : function(arr) {
362− return function(f) {
363− var result = [];
364− var l = arr.length;
365− for (var i = 0; i < l; i++) {
366− var xs = f(arr[i]);
367− var k = xs.length;
368− for (var j = 0; j < k; j++) {
369− result.push(xs[j]);
370− }
371− }
372− return result;
373− };
374− };
375−
376− // output/Control.Bind/index.js
377− var identity2 = /* @__PURE__ */ identity(categoryFn);
378− var discard = function(dict) {
379− return dict.discard;
380− };
381− var bindArray = {
382− bind: arrayBind,
383− Apply0: function() {
384− return applyArray;
385− }
386− };
387− var bind = function(dict) {
388− return dict.bind;
389− };
390− var bindFlipped = function(dictBind) {
391− return flip(bind(dictBind));
392− };
393− var discardUnit = {
394− discard: function(dictBind) {
395− return bind(dictBind);
396− }
397− };
398− var join = function(dictBind) {
399− var bind1 = bind(dictBind);
400− return function(m) {
401− return bind1(m)(identity2);
402− };
403− };
404−
405− // output/Control.Monad.ST.Internal/foreign.js
406− var map_ = function(f) {
407− return function(a) {
408− return function() {
409− return f(a());
410− };
411− };
412− };
413− var pure_ = function(a) {
414− return function() {
415− return a;
416− };
417− };
418− var bind_ = function(a) {
419− return function(f) {
420− return function() {
421− return f(a())();
422− };
423− };
424− };
425− function newSTRef(val) {
426− return function() {
427− return { value: val };
428− };
429− }
430− var read = function(ref) {
431− return function() {
432− return ref.value;
433− };
434− };
435− var modifyImpl = function(f) {
436− return function(ref) {
437− return function() {
438− var t = f(ref.value);
439− ref.value = t.state;
440− return t.value;
441− };
442− };
443− };
444− var write = function(a) {
445− return function(ref) {
446− return function() {
447− return ref.value = a;
448− };
449− };
450− };
451−
452− // output/Control.Monad/index.js
453− var ap = function(dictMonad) {
454− var bind3 = bind(dictMonad.Bind1());
455− var pure4 = pure(dictMonad.Applicative0());
456− return function(f) {
457− return function(a) {
458− return bind3(f)(function(f$prime) {
459− return bind3(a)(function(a$prime) {
460− return pure4(f$prime(a$prime));
461− });
462− });
463− };
464− };
465− };
466−
467− // output/Data.Either/index.js
468− var Left = /* @__PURE__ */ function() {
469− function Left2(value0) {
470− this.value0 = value0;
471− }
472− ;
473− Left2.create = function(value0) {
474− return new Left2(value0);
475− };
476− return Left2;
477− }();
478− var Right = /* @__PURE__ */ function() {
479− function Right2(value0) {
480− this.value0 = value0;
481− }
482− ;
483− Right2.create = function(value0) {
484− return new Right2(value0);
485− };
486− return Right2;
487− }();
488−
489− // output/Data.Monoid/index.js
490− var mempty = function(dict) {
491− return dict.mempty;
492− };
493−
494− // output/Effect/foreign.js
495− var pureE = function(a) {
496− return function() {
497− return a;
498− };
499− };
500− var bindE = function(a) {
501− return function(f) {
502− return function() {
503− return f(a())();
504− };
505− };
506− };
507−
508− // output/Effect/index.js
509− var $runtime_lazy = function(name2, moduleName, init3) {
510− var state2 = 0;
511− var val;
512− return function(lineNumber) {
513− if (state2 === 2) return val;
514− if (state2 === 1) throw new ReferenceError(name2 + " was needed before it finished initializing (module " + moduleName + ", line " + lineNumber + ")", moduleName, lineNumber);
515− state2 = 1;
516− val = init3();
517− state2 = 2;
518− return val;
519− };
520− };
521− var monadEffect = {
522− Applicative0: function() {
523− return applicativeEffect;
524− },
525− Bind1: function() {
526− return bindEffect;
527− }
528− };
529− var bindEffect = {
530− bind: bindE,
531− Apply0: function() {
532− return $lazy_applyEffect(0);
533− }
534− };
535− var applicativeEffect = {
536− pure: pureE,
537− Apply0: function() {
538− return $lazy_applyEffect(0);
539− }
540− };
541− var $lazy_functorEffect = /* @__PURE__ */ $runtime_lazy("functorEffect", "Effect", function() {
542− return {
543− map: liftA1(applicativeEffect)
544− };
545− });
546− var $lazy_applyEffect = /* @__PURE__ */ $runtime_lazy("applyEffect", "Effect", function() {
547− return {
548− apply: ap(monadEffect),
549− Functor0: function() {
550− return $lazy_functorEffect(0);
551− }
552− };
553− });
554−
555− // output/Control.Monad.ST.Internal/index.js
556− var $runtime_lazy2 = function(name2, moduleName, init3) {
557− var state2 = 0;
558− var val;
559− return function(lineNumber) {
560− if (state2 === 2) return val;
561− if (state2 === 1) throw new ReferenceError(name2 + " was needed before it finished initializing (module " + moduleName + ", line " + lineNumber + ")", moduleName, lineNumber);
562− state2 = 1;
563− val = init3();
564− state2 = 2;
565− return val;
566− };
567− };
568− var modify$prime = modifyImpl;
569− var modify = function(f) {
570− return modify$prime(function(s) {
571− var s$prime = f(s);
572− return {
573− state: s$prime,
574− value: s$prime
575− };
576− });
577− };
578− var functorST = {
579− map: map_
580− };
581− var monadST = {
582− Applicative0: function() {
583− return applicativeST;
584− },
585− Bind1: function() {
586− return bindST;
587− }
588− };
589− var bindST = {
590− bind: bind_,
591− Apply0: function() {
592− return $lazy_applyST(0);
593− }
594− };
595− var applicativeST = {
596− pure: pure_,
597− Apply0: function() {
598− return $lazy_applyST(0);
599− }
600− };
601− var $lazy_applyST = /* @__PURE__ */ $runtime_lazy2("applyST", "Control.Monad.ST.Internal", function() {
602− return {
603− apply: ap(monadST),
604− Functor0: function() {
605− return functorST;
606− }
607− };
608− });
609− var applyST = /* @__PURE__ */ $lazy_applyST(47);
610−
611− // output/Data.Array/foreign.js
612− var replicateFill = function(count, value) {
613− if (count < 1) {
614− return [];
615− }
616− var result = new Array(count);
617− return result.fill(value);
618− };
619− var replicatePolyfill = function(count, value) {
620− var result = [];
621− var n = 0;
622− for (var i = 0; i < count; i++) {
623− result[n++] = value;
624− }
625− return result;
626− };
627− var replicateImpl = typeof Array.prototype.fill === "function" ? replicateFill : replicatePolyfill;
628− var length = function(xs) {
629− return xs.length;
630− };
631− var unconsImpl = function(empty4, next2, xs) {
632− return xs.length === 0 ? empty4({}) : next2(xs[0])(xs.slice(1));
633− };
634− var indexImpl = function(just, nothing, xs, i) {
635− return i < 0 || i >= xs.length ? nothing : just(xs[i]);
636− };
637− var filterImpl = function(f, xs) {
638− return xs.filter(f);
639− };
640− var partitionImpl = function(f, xs) {
641− var yes = [];
642− var no = [];
643− for (var i = 0; i < xs.length; i++) {
644− var x = xs[i];
645− if (f(x))
646− yes.push(x);
647− else
648− no.push(x);
649− }
650− return { yes, no };
651− };
652−
653− // output/Data.Array.ST/foreign.js
654− function newSTArray() {
655− return [];
656− }
657− function unsafeFreezeThawImpl(xs) {
658− return xs;
659− }
660− var unsafeFreezeImpl = unsafeFreezeThawImpl;
661− var pushImpl = function(a, xs) {
662− return xs.push(a);
663− };
664−
665− // output/Control.Monad.ST.Uncurried/foreign.js
666− var runSTFn1 = function runSTFn12(fn) {
667− return function(a) {
668− return function() {
669− return fn(a);
670− };
671− };
672− };
673− var runSTFn2 = function runSTFn22(fn) {
674− return function(a) {
675− return function(b) {
676− return function() {
677− return fn(a, b);
678− };
679− };
680− };
681− };
682−
683− // output/Data.Array.ST/index.js
684− var unsafeFreeze = /* @__PURE__ */ runSTFn1(unsafeFreezeImpl);
685− var push = /* @__PURE__ */ runSTFn2(pushImpl);
686−
687− // output/Data.HeytingAlgebra/foreign.js
688− var boolConj = function(b1) {
689− return function(b2) {
690− return b1 && b2;
691− };
692− };
693− var boolDisj = function(b1) {
694− return function(b2) {
695− return b1 || b2;
696− };
697− };
698− var boolNot = function(b) {
699− return !b;
700− };
701−
702− // output/Data.HeytingAlgebra/index.js
703− var not = function(dict) {
704− return dict.not;
705− };
706− var disj = function(dict) {
707− return dict.disj;
708− };
709− var heytingAlgebraBoolean = {
710− ff: false,
711− tt: true,
712− implies: function(a) {
713− return function(b) {
714− return disj(heytingAlgebraBoolean)(not(heytingAlgebraBoolean)(a))(b);
715− };
716− },
717− conj: boolConj,
718− disj: boolDisj,
719− not: boolNot
720− };
721−
722− // output/Data.Array.ST.Iterator/index.js
723− var map3 = /* @__PURE__ */ map(functorST);
724− var not2 = /* @__PURE__ */ not(heytingAlgebraBoolean);
725− var $$void2 = /* @__PURE__ */ $$void(functorST);
726− var Iterator = /* @__PURE__ */ function() {
727− function Iterator2(value0, value1) {
728− this.value0 = value0;
729− this.value1 = value1;
730− }
731− ;
732− Iterator2.create = function(value0) {
733− return function(value1) {
734− return new Iterator2(value0, value1);
735− };
736− };
737− return Iterator2;
738− }();
739− var next = function(v) {
740− return function __do2() {
741− var i = read(v.value1)();
742− modify(function(v1) {
743− return v1 + 1 | 0;
744− })(v.value1)();
745− return v.value0(i);
746− };
747− };
748− var iterator = function(f) {
749− return map3(Iterator.create(f))(newSTRef(0));
750− };
751− var iterate = function(iter) {
752− return function(f) {
753− return function __do2() {
754− var $$break = newSTRef(false)();
755− while (map3(not2)(read($$break))()) {
756− (function __do3() {
757− var mx = next(iter)();
758− if (mx instanceof Just) {
759− return f(mx.value0)();
760− }
761− ;
762− if (mx instanceof Nothing) {
763− return $$void2(write(true)($$break))();
764− }
765− ;
766− throw new Error("Failed pattern match at Data.Array.ST.Iterator (line 42, column 5 - line 44, column 47): " + [mx.constructor.name]);
767− })();
768− }
769− ;
770− return {};
771− };
772− };
773− };
774−
775− // output/Data.Foldable/foreign.js
776− var foldrArray = function(f) {
777− return function(init3) {
778− return function(xs) {
779− var acc = init3;
780− var len = xs.length;
781− for (var i = len - 1; i >= 0; i--) {
782− acc = f(xs[i])(acc);
783− }
784− return acc;
785− };
786− };
787− };
788− var foldlArray = function(f) {
789− return function(init3) {
790− return function(xs) {
791− var acc = init3;
792− var len = xs.length;
793− for (var i = 0; i < len; i++) {
794− acc = f(acc)(xs[i]);
795− }
796− return acc;
797− };
798− };
799− };
800−
801− // output/Control.Plus/index.js
802− var empty2 = function(dict) {
803− return dict.empty;
804− };
805−
806− // output/Unsafe.Coerce/foreign.js
807− var unsafeCoerce2 = function(x) {
808− return x;
809− };
810−
811− // output/Safe.Coerce/index.js
812− var coerce = function() {
813− return unsafeCoerce2;
814− };
815−
816− // output/Data.Newtype/index.js
817− var coerce2 = /* @__PURE__ */ coerce();
818− var wrap = function() {
819− return coerce2;
820− };
821−
822− // output/Data.Foldable/index.js
823− var foldr = function(dict) {
824− return dict.foldr;
825− };
826− var foldl = function(dict) {
827− return dict.foldl;
828− };
829− var foldMapDefaultR = function(dictFoldable) {
830− var foldr2 = foldr(dictFoldable);
831− return function(dictMonoid) {
832− var append3 = append(dictMonoid.Semigroup0());
833− var mempty2 = mempty(dictMonoid);
834− return function(f) {
835− return foldr2(function(x) {
836− return function(acc) {
837− return append3(f(x))(acc);
838− };
839− })(mempty2);
840− };
841− };
842− };
843− var foldableArray = {
844− foldr: foldrArray,
845− foldl: foldlArray,
846− foldMap: function(dictMonoid) {
847− return foldMapDefaultR(foldableArray)(dictMonoid);
848− }
849− };
850−
851− // output/Data.Function.Uncurried/foreign.js
852− var runFn2 = function(fn) {
853− return function(a) {
854− return function(b) {
855− return fn(a, b);
856− };
857− };
858− };
859− var runFn3 = function(fn) {
860− return function(a) {
861− return function(b) {
862− return function(c) {
863− return fn(a, b, c);
864− };
865− };
866− };
867− };
868− var runFn4 = function(fn) {
869− return function(a) {
870− return function(b) {
871− return function(c) {
872− return function(d) {
873− return fn(a, b, c, d);
874− };
875− };
876− };
877− };
878− };
879−
880− // output/Data.Array/index.js
881− var uncons = /* @__PURE__ */ function() {
882− return runFn3(unconsImpl)($$const(Nothing.value))(function(x) {
883− return function(xs) {
884− return new Just({
885− head: x,
886− tail: xs
887− });
888− };
889− });
890− }();
891− var singleton2 = function(a) {
892− return [a];
893− };
894− var partition = /* @__PURE__ */ runFn2(partitionImpl);
895− var $$null = function(xs) {
896− return length(xs) === 0;
897− };
898− var index = /* @__PURE__ */ function() {
899− return runFn4(indexImpl)(Just.create)(Nothing.value);
900− }();
901− var filter = /* @__PURE__ */ runFn2(filterImpl);
902− var concatMap = /* @__PURE__ */ flip(/* @__PURE__ */ bind(bindArray));
903− var mapMaybe = function(f) {
904− return concatMap(function() {
905− var $189 = maybe([])(singleton2);
906− return function($190) {
907− return $189(f($190));
908− };
909− }());
910− };
911−
912− // output/Foreign.Object.ST/foreign.js
913− function poke2(k) {
914− return function(v) {
915− return function(m) {
916− return function() {
917− m[k] = v;
918− return m;
919− };
920− };
921− };
922− }
923−
924− // output/Foreign.Object/index.js
925− var thawST = _copyST;
926− var mutate = function(f) {
927− return function(m) {
928− return runST(function __do2() {
929− var s = thawST(m)();
930− f(s)();
931− return s;
932− });
933− };
934− };
935− var insert = function(k) {
936− return function(v) {
937− return mutate(poke2(k)(v));
938− };
939− };
940−
941− // output/Data.Int/foreign.js
942− var toNumber = function(n) {
943− return n;
944− };
945−
946− // output/Data.String.CodePoints/foreign.js
947− var hasArrayFrom = typeof Array.from === "function";
948− var hasStringIterator = typeof Symbol !== "undefined" && Symbol != null && typeof Symbol.iterator !== "undefined" && typeof String.prototype[Symbol.iterator] === "function";
949− var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function";
950− var hasCodePointAt = typeof String.prototype.codePointAt === "function";
951−
952− // output/Data.String.Common/foreign.js
953− var split = function(sep) {
954− return function(s) {
955− return s.split(sep);
956− };
957− };
958−
959− // output/Data.String.Common/index.js
960− var $$null2 = function(s) {
961− return s === "";
962− };
963−
964− // output/Data.Argonaut.Encode.Encoders/index.js
965− var encodeNumber = id;
966− var encodeInt = function($53) {
967− return id(toNumber($53));
968− };
969−
970− // output/Record/index.js
971− var get = function(dictIsSymbol) {
972− var reflectSymbol2 = reflectSymbol(dictIsSymbol);
973− return function() {
974− return function(l) {
975− return function(r) {
976− return unsafeGet(reflectSymbol2(l))(r);
977− };
978− };
979− };
980− };
981− var $$delete3 = function(dictIsSymbol) {
982− var reflectSymbol2 = reflectSymbol(dictIsSymbol);
983− return function() {
984− return function() {
985− return function(l) {
986− return function(r) {
987− return unsafeDelete(reflectSymbol2(l))(r);
988− };
989− };
990− };
991− };
992− };
993−
994− // output/Data.Argonaut.Encode.Class/index.js
995− var gEncodeJsonNil = {
996− gEncodeJson: function(v) {
997− return function(v1) {
998− return empty;
999− };
1000− }
1001− };
1002− var gEncodeJson = function(dict) {
1003− return dict.gEncodeJson;
1004− };
1005− var encodeRecord = function(dictGEncodeJson) {
1006− var gEncodeJson1 = gEncodeJson(dictGEncodeJson);
1007− return function() {
1008− return {
1009− encodeJson: function(rec) {
1010− return id(gEncodeJson1(rec)($$Proxy.value));
1011− }
1012− };
1013− };
1014− };
1015− var encodeJsonJNumber = {
1016− encodeJson: encodeNumber
1017− };
1018− var encodeJsonInt = {
1019− encodeJson: encodeInt
1020− };
1021− var encodeJson = function(dict) {
1022− return dict.encodeJson;
1023− };
1024− var gEncodeJsonCons = function(dictEncodeJson) {
1025− var encodeJson1 = encodeJson(dictEncodeJson);
1026− return function(dictGEncodeJson) {
1027− var gEncodeJson1 = gEncodeJson(dictGEncodeJson);
1028− return function(dictIsSymbol) {
1029− var reflectSymbol2 = reflectSymbol(dictIsSymbol);
1030− var get2 = get(dictIsSymbol)();
1031− return function() {
1032− return {
1033− gEncodeJson: function(row) {
1034− return function(v) {
1035− return insert(reflectSymbol2($$Proxy.value))(encodeJson1(get2($$Proxy.value)(row)))(gEncodeJson1(row)($$Proxy.value));
1036− };
1037− }
1038− };
1039− };
1040− };
1041− };
1042− };
1043−
1044− // output/Effect.Console/foreign.js
1045− var log2 = function(s) {
1046− return function() {
1047− console.log(s);
1048− };
1049− };
1050−
1051− // output/Bun.Request/foreign.js
1052− function method(req) {
1053− return req.method;
1054− }
1055− function url(req) {
1056− return req.url;
1057− }
1058−
1059− // output/Bun.Response/foreign.js
1060− function json2(j) {
1061− return function(options) {
1062− return Response.json(j, options);
1063− };
1064− }
1065− function string(s) {
1066− return function(options) {
1067− return new Response(s, options);
1068− };
1069− }
1070−
1071− // output/Data.URL/foreign.js
1072− var fromStringImpl2 = (s) => {
1073− try {
1074− return new URL(s);
1075− } catch {
1076− return null;
1077− }
1078− };
1079− var pathnameImpl = (u) => u.pathname;
1080−
1081− // output/Data.Compactable/index.js
1082− var $$void3 = /* @__PURE__ */ $$void(functorST);
1083− var pure1 = /* @__PURE__ */ pure(applicativeST);
1084− var apply2 = /* @__PURE__ */ apply(applyST);
1085− var map4 = /* @__PURE__ */ map(functorST);
1086− var compactableMaybe = {
1087− compact: /* @__PURE__ */ join(bindMaybe),
1088− separate: function(v) {
1089− if (v instanceof Nothing) {
1090− return {
1091− left: Nothing.value,
1092− right: Nothing.value
1093− };
1094− }
1095− ;
1096− if (v instanceof Just) {
1097− if (v.value0 instanceof Left) {
1098− return {
1099− left: new Just(v.value0.value0),
1100− right: Nothing.value
1101− };
1102− }
1103− ;
1104− if (v.value0 instanceof Right) {
1105− return {
1106− left: Nothing.value,
1107− right: new Just(v.value0.value0)
1108− };
1109− }
1110− ;
1111− throw new Error("Failed pattern match at Data.Compactable (line 91, column 23 - line 93, column 48): " + [v.value0.constructor.name]);
1112− }
1113− ;
1114− throw new Error("Failed pattern match at Data.Compactable (line 87, column 1 - line 93, column 48): " + [v.constructor.name]);
1115− }
1116− };
1117− var compactableArray = {
1118− compact: function(xs) {
1119− return function __do2() {
1120− var result = newSTArray();
1121− var iter = iterator(function(v) {
1122− return index(xs)(v);
1123− })();
1124− iterate(iter)(function($108) {
1125− return $$void3(function(v) {
1126− if (v instanceof Nothing) {
1127− return pure1(0);
1128− }
1129− ;
1130− if (v instanceof Just) {
1131− return push(v.value0)(result);
1132− }
1133− ;
1134− throw new Error("Failed pattern match at Data.Compactable (line 111, column 34 - line 113, column 35): " + [v.constructor.name]);
1135− }($108));
1136− })();
1137− return unsafeFreeze(result)();
1138− }();
1139− },
1140− separate: function(xs) {
1141− return function __do2() {
1142− var ls = newSTArray();
1143− var rs = newSTArray();
1144− var iter = iterator(function(v) {
1145− return index(xs)(v);
1146− })();
1147− iterate(iter)(function($109) {
1148− return $$void3(function(v) {
1149− if (v instanceof Left) {
1150− return push(v.value0)(ls);
1151− }
1152− ;
1153− if (v instanceof Right) {
1154− return push(v.value0)(rs);
1155− }
1156− ;
1157− throw new Error("Failed pattern match at Data.Compactable (line 122, column 34 - line 124, column 31): " + [v.constructor.name]);
1158− }($109));
1159− })();
1160− return apply2(map4(function(v) {
1161− return function(v1) {
1162− return {
1163− left: v,
1164− right: v1
1165− };
1166− };
1167− })(unsafeFreeze(ls)))(unsafeFreeze(rs))();
1168− }();
1169− }
1170− };
1171−
1172− // output/Data.Filterable/index.js
1173− var append2 = /* @__PURE__ */ append(semigroupArray);
1174− var foldl2 = /* @__PURE__ */ foldl(foldableArray);
1175− var partitionMap = function(dict) {
1176− return dict.partitionMap;
1177− };
1178− var maybeBool = function(p) {
1179− return function(x) {
1180− var $66 = p(x);
1181− if ($66) {
1182− return new Just(x);
1183− }
1184− ;
1185− return Nothing.value;
1186− };
1187− };
1188− var filterableArray = {
1189− partitionMap: function(p) {
1190− var go = function(acc) {
1191− return function(x) {
1192− var v = p(x);
1193− if (v instanceof Left) {
1194− return {
1195− right: acc.right,
1196− left: append2(acc.left)([v.value0])
1197− };
1198− }
1199− ;
1200− if (v instanceof Right) {
1201− return {
1202− left: acc.left,
1203− right: append2(acc.right)([v.value0])
1204− };
1205− }
1206− ;
1207− throw new Error("Failed pattern match at Data.Filterable (line 149, column 16 - line 151, column 50): " + [v.constructor.name]);
1208− };
1209− };
1210− return foldl2(go)({
1211− left: [],
1212− right: []
1213− });
1214− },
1215− partition,
1216− filterMap: mapMaybe,
1217− filter,
1218− Compactable0: function() {
1219− return compactableArray;
1220− },
1221− Functor1: function() {
1222− return functorArray;
1223− }
1224− };
1225− var filterMap = function(dict) {
1226− return dict.filterMap;
1227− };
1228− var filterDefault = function(dictFilterable) {
1229− var $121 = filterMap(dictFilterable);
1230− return function($122) {
1231− return $121(maybeBool($122));
1232− };
1233− };
1234− var filter3 = function(dict) {
1235− return dict.filter;
1236− };
1237− var eitherBool = function(p) {
1238− return function(x) {
1239− var $84 = p(x);
1240− if ($84) {
1241− return new Right(x);
1242− }
1243− ;
1244− return new Left(x);
1245− };
1246− };
1247− var partitionDefault = function(dictFilterable) {
1248− var partitionMap1 = partitionMap(dictFilterable);
1249− return function(p) {
1250− return function(xs) {
1251− var o = partitionMap1(eitherBool(p))(xs);
1252− return {
1253− no: o.left,
1254− yes: o.right
1255− };
1256− };
1257− };
1258− };
1259− var filterableMaybe = {
1260− partitionMap: function(v) {
1261− return function(v1) {
1262− if (v1 instanceof Nothing) {
1263− return {
1264− left: Nothing.value,
1265− right: Nothing.value
1266− };
1267− }
1268− ;
1269− if (v1 instanceof Just) {
1270− var v2 = v(v1.value0);
1271− if (v2 instanceof Left) {
1272− return {
1273− left: new Just(v2.value0),
1274− right: Nothing.value
1275− };
1276− }
1277− ;
1278− if (v2 instanceof Right) {
1279− return {
1280− left: Nothing.value,
1281− right: new Just(v2.value0)
1282− };
1283− }
1284− ;
1285− throw new Error("Failed pattern match at Data.Filterable (line 161, column 29 - line 163, column 48): " + [v2.constructor.name]);
1286− }
1287− ;
1288− throw new Error("Failed pattern match at Data.Filterable (line 159, column 1 - line 169, column 29): " + [v.constructor.name, v1.constructor.name]);
1289− };
1290− },
1291− partition: function(p) {
1292− return partitionDefault(filterableMaybe)(p);
1293− },
1294− filterMap: /* @__PURE__ */ bindFlipped(bindMaybe),
1295− filter: function(p) {
1296− return filterDefault(filterableMaybe)(p);
1297− },
1298− Compactable0: function() {
1299− return compactableMaybe;
1300− },
1301− Functor1: function() {
1302− return functorMaybe;
1303− }
1304− };
1305−
1306− // output/Data.Nullable/foreign.js
1307− function nullable(a, r, f) {
1308− return a == null ? r : f(a);
1309− }
1310−
1311− // output/Data.Nullable/index.js
1312− var toMaybe = function(n) {
1313− return nullable(n, Nothing.value, Just.create);
1314− };
1315−
1316− // output/Data.String.Utils/foreign.js
1317− function startsWithImpl(searchString, s) {
1318− return s.startsWith(searchString);
1319− }
1320−
1321− // output/Data.String.Utils/index.js
1322− var startsWith = function(searchString) {
1323− return function(s) {
1324− return startsWithImpl(searchString, s);
1325− };
1326− };
1327−
1328− // output/Foreign/foreign.js
1329− var isArray = Array.isArray || function(value) {
1330− return Object.prototype.toString.call(value) === "[object Array]";
1331− };
1332−
1333− // output/Data.URL/index.js
1334− var filter4 = /* @__PURE__ */ filter3(filterableMaybe);
1335− var wrap2 = /* @__PURE__ */ wrap();
1336− var filter1 = /* @__PURE__ */ filter3(filterableArray);
1337− var PathEmpty = /* @__PURE__ */ function() {
1338− function PathEmpty2() {
1339− }
1340− ;
1341− PathEmpty2.value = new PathEmpty2();
1342− return PathEmpty2;
1343− }();
1344− var PathAbsolute = /* @__PURE__ */ function() {
1345− function PathAbsolute2(value0) {
1346− this.value0 = value0;
1347− }
1348− ;
1349− PathAbsolute2.create = function(value0) {
1350− return new PathAbsolute2(value0);
1351− };
1352− return PathAbsolute2;
1353− }();
1354− var PathRelative = /* @__PURE__ */ function() {
1355− function PathRelative2(value0) {
1356− this.value0 = value0;
1357− }
1358− ;
1359− PathRelative2.create = function(value0) {
1360− return new PathRelative2(value0);
1361− };
1362− return PathRelative2;
1363− }();
1364− var pathFromString = function(s) {
1365− var segments = function() {
1366− var $235 = filter1(function($238) {
1367− return !$$null2($238);
1368− });
1369− var $236 = split(wrap2("/"));
1370− return function($237) {
1371− return $235($236($237));
1372− };
1373− }();
1374− return maybe(PathEmpty.value)(function() {
1375− var $203 = startsWith("/")(s);
1376− if ($203) {
1377− return PathAbsolute.create;
1378− }
1379− ;
1380− return PathRelative.create;
1381− }())(filter4(function($239) {
1382− return !$$null($239);
1383− })(new Just(segments(s))));
1384− };
1385− var path = function($240) {
1386− return pathFromString(pathnameImpl($240));
1387− };
1388− var fromString2 = function($255) {
1389− return toMaybe(fromStringImpl2($255));
1390− };
1391−
1392− // output/PureStack.Router/index.js
1393− var bind2 = /* @__PURE__ */ bind(bindMaybe);
1394− var pure2 = /* @__PURE__ */ pure(applicativeEffect);
1395− var discard2 = /* @__PURE__ */ discard(discardUnit)(bindMaybe);
1396− var eq12 = /* @__PURE__ */ eq(/* @__PURE__ */ eqArray(eqString));
1397− var pure12 = /* @__PURE__ */ pure(applicativeMaybe);
1398− var toResponseRecord = function(dictEncodeJson) {
1399− var encodeJson2 = encodeJson(dictEncodeJson);
1400− return {
1401− toResponse: function(rec) {
1402− return function(opts) {
1403− return json2(encodeJson2(rec))(opts);
1404− };
1405− }
1406− };
1407− };
1408− var toResponse = function(dict) {
1409− return dict.toResponse;
1410− };
1411− var runAPI = function(dict) {
1412− return dict.runAPI;
1413− };
1414− var routeFromRequest = function(req) {
1415− return {
1416− verb: method(req),
1417− path: function() {
1418− var v = fromString2(url(req));
1419− if (v instanceof Nothing) {
1420− return [];
1421− }
1422− ;
1423− if (v instanceof Just) {
1424− var v1 = path(v.value0);
1425− if (v1 instanceof PathEmpty) {
1426− return [];
1427− }
1428− ;
1429− if (v1 instanceof PathAbsolute) {
1430− return v1.value0;
1431− }
1432− ;
1433− if (v1 instanceof PathRelative) {
1434− return v1.value0;
1435− }
1436− ;
1437− throw new Error("Failed pattern match at PureStack.Router (line 68, column 19 - line 71, column 32): " + [v1.constructor.name]);
1438− }
1439− ;
1440− throw new Error("Failed pattern match at PureStack.Router (line 66, column 11 - line 71, column 32): " + [v.constructor.name]);
1441− }()
1442− };
1443− };
1444− var run3 = function() {
1445− return function(dictRunAPI) {
1446− var runAPI1 = runAPI(dictRunAPI);
1447− return function(handlers) {
1448− return function(req) {
1449− return runAPI1(handlers)(routeFromRequest(req))(req);
1450− };
1451− };
1452− };
1453− };
1454− var parseRoute = function(dict) {
1455− return dict.parseRoute;
1456− };
1457− var runAPICons = function(dictParseRoute) {
1458− var parseRoute1 = parseRoute(dictParseRoute);
1459− return function() {
1460− return function(dictRunAPI) {
1461− var runAPI1 = runAPI(dictRunAPI);
1462− return function(dictIsSymbol) {
1463− var $$delete4 = $$delete3(dictIsSymbol)()();
1464− var get2 = get(dictIsSymbol)();
1465− return function() {
1466− return function() {
1467− return {
1468− runAPI: function(handlers) {
1469− return function(v) {
1470− return function(req) {
1471− var v1 = parseRoute1({
1472− path: v.path,
1473− verb: v.verb
1474− });
1475− if (v1 instanceof Nothing) {
1476− return runAPI1($$delete4($$Proxy.value)(handlers))(v)(req);
1477− }
1478− ;
1479− if (v1 instanceof Just) {
1480− return v1.value0(get2($$Proxy.value)(handlers))(req);
1481− }
1482− ;
1483− throw new Error("Failed pattern match at PureStack.Router (line 150, column 5 - line 152, column 59): " + [v1.constructor.name]);
1484− };
1485− };
1486− }
1487− };
1488− };
1489− };
1490− };
1491− };
1492− };
1493− };
1494− var notFound = /* @__PURE__ */ string("")({
1495− status: 404,
1496− statusText: "Not Found",
1497− headers: []
1498− });
1499− var runAPINilRow = {
1500− runAPI: function(v) {
1501− return function(v1) {
1502− return function(v2) {
1503− return pure2(notFound);
1504− };
1505− };
1506− }
1507− };
1508− var assert = function(dictApplicative) {
1509− var pure22 = pure(dictApplicative);
1510− return function(dictPlus) {
1511− var empty4 = empty2(dictPlus);
1512− return function(v) {
1513− if (v) {
1514− return pure22(unit);
1515− }
1516− ;
1517− if (!v) {
1518− return empty4;
1519− }
1520− ;
1521− throw new Error("Failed pattern match at PureStack.Router (line 130, column 1 - line 130, column 65): " + [v.constructor.name]);
1522− };
1523− };
1524− };
1525− var assert1 = /* @__PURE__ */ assert(applicativeMaybe)(plusMaybe);
1526− var parseRoute$div = function(dictParseRoute) {
1527− var parseRoute1 = parseRoute(dictParseRoute);
1528− return function(dictIsSymbol) {
1529− var reflectSymbol2 = reflectSymbol(dictIsSymbol);
1530− return {
1531− parseRoute: function(v) {
1532− return bind2(uncons(v.path))(function(v1) {
1533− return discard2(assert1(v1.head === reflectSymbol2($$Proxy.value)))(function() {
1534− return parseRoute1({
1535− path: v1.tail,
1536− verb: v.verb
1537− });
1538− });
1539− });
1540− }
1541− };
1542− };
1543− };
1544− var parseRouteGETEffect = function(dictToResponse) {
1545− var toResponse1 = toResponse(dictToResponse);
1546− return {
1547− parseRoute: function(v) {
1548− return discard2(assert1(v.verb === "GET"))(function() {
1549− return discard2(assert1(eq12(v.path)([])))(function() {
1550− return pure12(function(handler) {
1551− return function(_req) {
1552− return function __do2() {
1553− var resp = handler();
1554− return toResponse1(resp)({
1555− status: 200,
1556− statusText: "OK",
1557− headers: []
1558− });
1559− };
1560− };
1561− });
1562− });
1563− });
1564− }
1565− };
1566− };
1567− var parseRoutePOSTUnitEffect = function(dictToResponse) {
1568− var toResponse1 = toResponse(dictToResponse);
1569− return {
1570− parseRoute: function(v) {
1571− return discard2(assert1(eq12(v.path)([])))(function() {
1572− return discard2(assert1(v.verb === "POST"))(function() {
1573− return pure12(function(handler) {
1574− return function(req) {
1575− return function __do2() {
1576− var resp = handler();
1577− return toResponse1(resp)({
1578− status: 200,
1579− statusText: "OK",
1580− headers: []
1581− });
1582− };
1583− };
1584− });
1585− });
1586− });
1587− }
1588− };
1589− };
1590−
1591− // output/Main/index.js
1592− var gEncodeJsonCons2 = /* @__PURE__ */ gEncodeJsonCons(encodeJsonInt);
1593− var aIsSymbol = {
1594− reflectSymbol: function() {
1595− return "a";
1596− }
1597− };
1598− var fooIsSymbol = {
1599− reflectSymbol: function() {
1600− return "foo";
1601− }
1602− };
1603− var barIsSymbol = {
1604− reflectSymbol: function() {
1605− return "bar";
1606− }
1607− };
1608− var run4 = /* @__PURE__ */ run3()(/* @__PURE__ */ runAPICons(/* @__PURE__ */ parseRoute$div(/* @__PURE__ */ parseRoute$div(/* @__PURE__ */ parseRoutePOSTUnitEffect(/* @__PURE__ */ toResponseRecord(/* @__PURE__ */ encodeRecord(/* @__PURE__ */ gEncodeJsonCons2(gEncodeJsonNil)(aIsSymbol)())())))({
1609− reflectSymbol: function() {
1610− return "qux";
1611− }
1612− }))(fooIsSymbol))()(/* @__PURE__ */ runAPICons(/* @__PURE__ */ parseRoute$div(/* @__PURE__ */ parseRoute$div(/* @__PURE__ */ parseRoute$div(/* @__PURE__ */ parseRouteGETEffect(/* @__PURE__ */ toResponseRecord(/* @__PURE__ */ encodeRecord(/* @__PURE__ */ gEncodeJsonCons2(/* @__PURE__ */ gEncodeJsonCons(encodeJsonJNumber)(gEncodeJsonNil)({
1613− reflectSymbol: function() {
1614− return "b";
1615− }
1616− })())(aIsSymbol)())())))(fooIsSymbol))(barIsSymbol))(fooIsSymbol))()(runAPINilRow)(fooIsSymbol)()())(barIsSymbol)()());
1617− var pure3 = /* @__PURE__ */ pure(applicativeEffect);
1618− var main = function __do() {
1619− log2("\u{1F35D}")();
1620− return serve(run4({
1621− foo: pure3({
1622− a: 1,
1623− b: 1
1624− }),
1625− bar: pure3({
1626− a: 8
1627− })
1628− }))();
1629− };
1630−
1631− // <stdin>
1632− main();
1633−})();
spago.dhall+16−2
@@ -1,18 +1,32 @@
1{ name = "my-project"
1{ name = "my-project"
2, dependencies =
2, dependencies =
3+ [ "aff"
3− [ "aff-promise"
4+ , "aff-promise"
4 , "argonaut"
5 , "argonaut"
5 , "arrays"
6 , "arrays"
6 , "console"
7 , "console"
7 , "control"
8 , "control"
9+ , "datetime"
8 , "effect"
10 , "effect"
11+ , "either"
12+ , "foldable-traversable"
13+ , "foreign-object"
14+ , "formatters"
15+ , "integers"
16+ , "lists"
9 , "maybe"
17 , "maybe"
18+ , "now"
19+ , "numbers"
20+ , "ordered-collections"
21+ , "parsing"
10 , "prelude"
22 , "prelude"
23+ , "profunctor-lenses"
11 , "record"
24 , "record"
12 , "run"
25 , "run"
26+ , "strings"
27+ , "transformers"
13 , "tuples"
28 , "tuples"
14 , "typelevel-prelude"
29 , "typelevel-prelude"
15− , "unsafe-coerce"
16 , "url-immutable"
30 , "url-immutable"
17 ]
31 ]
18, packages = ./packages.dhall
32, packages = ./packages.dhall
src/Bun/Request.js+6−2
@@ -1,5 +1,5 @@
1−export function json(req) {
1+export function jsonPromise(req) {
2− return req.json
2+ return req.json()
3}
3}
4
4
5export function method(req) {
5export function method(req) {
@@ -9,3 +9,7 @@ export function method(req) {
9export function url(req) {
9export function url(req) {
10 return req.url
10 return req.url
11}
11}
12+
13+export function headers(req) {
14+ return Object.fromEntries(req.headers.entries());
15+}
src/Bun/Request.purs+18−2
@@ -1,12 +1,28 @@
1−module Bun.Request where
1+module Bun.Request
2+ ( Request
3+ , method
4+ , url
5+ , json
6+ , headers
7+ ) where
8+
9+import Prelude
2
10
3import Control.Promise (Promise)
11import Control.Promise (Promise)
12+import Control.Promise as Promise
4import Data.Argonaut (Json)
13import Data.Argonaut (Json)
14+import Effect.Aff (Aff)
15+import Foreign.Object (Object)
5
16
6foreign import data Request :: Type
17foreign import data Request :: Type
7
18
8−foreign import json :: Request -> Promise Json
19+foreign import jsonPromise :: Request -> Promise Json
20+
21+json :: Request -> Aff Json
22+json = jsonPromise >>> Promise.toAff
9
23
10foreign import method :: Request -> String
24foreign import method :: Request -> String
11
25
12foreign import url :: Request -> String
26foreign import url :: Request -> String
27+
28+foreign import headers :: Request -> Object String
src/Bun/Response.js+23
@@ -10,3 +10,26 @@ export function string(s) {
10 return new Response(s, options)
10 return new Response(s, options)
11 }
11 }
12}
12}
13+
14+
15+export function stream(s) {
16+ return function(options) {
17+ return new Response(s, options)
18+ }
19+}
20+
21+export function body(resp) {
22+ return resp.body
23+}
24+
25+export function status(resp) {
26+ return resp.status
27+}
28+
29+export function statusText(resp) {
30+ return resp.statusText
31+}
32+
33+export function headers(resp) {
34+ return Object.fromEntries(resp.headers.entries());
35+}
src/Bun/Response.purs+9−3
@@ -1,12 +1,18 @@
1module Bun.Response where
1module Bun.Response where
2
2
3import Data.Argonaut (Json)
3import Data.Argonaut (Json)
4−import Data.Tuple.Nested (type (/\))
4+import Foreign.Object (Object)
5−import Effect (Effect)
6
5
7foreign import data Response :: Type
6foreign import data Response :: Type
7+foreign import data ReadableStream :: Type
8
8
9−type Options = { status :: Int, statusText :: String, headers :: Array (String /\ String) }
9+type Options = { status :: Int, statusText :: String, headers :: Object String }
10
10
11foreign import json :: Json -> Options -> Response
11foreign import json :: Json -> Options -> Response
12foreign import string :: String -> Options -> Response
12foreign import string :: String -> Options -> Response
13+foreign import stream :: ReadableStream -> Options -> Response
14+foreign import body :: Response -> ReadableStream
15+foreign import headers :: Response -> Object String
16+foreign import status :: Response -> Int
17+foreign import statusText :: Response -> String
18+
src/Bun/Server.js+1−1
@@ -1,4 +1,4 @@
1−export function serve(f) {
1+export function serve_(f) {
2 return function() {
2 return function() {
3 return Bun.serve({
3 return Bun.serve({
4 fetch(req) {
4 fetch(req) {
src/Bun/Server.purs+7−1
@@ -4,6 +4,12 @@ import Prelude
4
4
5import Bun.Request (Request)
5import Bun.Request (Request)
6import Bun.Response (Response)
6import Bun.Response (Response)
7+import Control.Promise (Promise)
8+import Control.Promise as Promise
7import Effect (Effect)
9import Effect (Effect)
10+import Effect.Aff (Aff)
8
11
12+foreign import serve_ :: (Request -> Effect (Promise Response)) -> Effect Unit
13+
9−foreign import serve :: (Request -> Effect Response) -> Effect Unit
14+serve :: (Request -> Aff Response) -> Effect Unit
15+serve f = serve_ (\req -> Promise.fromAff (f req))
src/Main.purs+23−5
@@ -3,17 +3,35 @@ module Main where
3import Prelude
3import Prelude
4
4
5import Bun.Server as Bun
5import Bun.Server as Bun
6+import Data.Maybe (Maybe)
6import Effect (Effect)
7import Effect (Effect)
8+import Effect.Aff (Aff)
7import Effect.Console (log)
9import Effect.Console (log)
8−import PureStack.Router (type (/), GET, POST)
10+import PureStack.Route (type (/), GET, Headers(..), Middleware, POST)
9−import PureStack.Router as Router
11+import PureStack.Route as Router
12+import PureStack.Server as Server
13+import Run (AFF, Run(..), runBaseAff)
14+import Type.Row (type (+))
15+
16+type Foo = { a :: String, b :: Maybe Int, c :: Array Number }
10
17
11type API =
18type API =
12− ( foo :: "foo" / "bar" / "foo" / GET { a :: Int, b :: Number }
19+ ( foo :: "foo" / "bar" / Foo / GET Unit { a :: Int, b :: Number, c :: Foo }
13− , bar :: "foo" / "qux" / POST Unit { a :: Int }
20+ , bar :: "foo" / Middleware "auth" / "qux" / POST (Headers { foo :: String } Unit) { a :: Int, s :: String }
21+ , deep ::
22+ "deep" /
23+ ( something :: "something" / GET Unit { a :: String }
24+ )
14 )
25 )
15
26
16main :: Effect Unit
27main :: Effect Unit
17main = do
28main = do
18 log "🍝"
29 log "🍝"
30+ Bun.serve $ Server.run @API identity
31+ { handlers:
19− Bun.serve $ Router.run @API { foo: pure { a: 1, b: 1.0 }, bar: pure { a: 8 } }
32+ { foo: \x -> pure { a: 1, b: 1.0, c: x }
33+ , bar: \i (Headers x _) -> pure { a: 8, s: x.foo }
34+ , deep: { something: pure { a: "something" } }
35+ }
36+ , middlewares: { auth: \req -> pure 1 }
37+ }
src/PureStack/Cookie.pursnew file+57
@@ -0,0 +1,57 @@
1+-- | This module allows parsing and generating cookie headers. You'll generally use
2+-- | the `Cookie.new` function to create a cookie from a name/value pair and the
3+-- | `Cookie.set*` functions to set attributes on a cookie.
4+-- |
5+-- | `Cookie.stringify` generates the string representation of a cookie, suitable for
6+-- | writing to an HTTP header.
7+-- |
8+-- | `Cookie.parse` parses the string representation of a cookie, returning an
9+-- | `Either ParseError Cookie`.
10+-- |
11+-- | ```purescript
12+-- | import PureStack.Cookie as Cookie
13+-- |
14+-- | > Cookie.stringify $ Cookie.setSecure $ Cookie.new "key" "value"
15+-- | key=value; Secure
16+-- |
17+-- | > Cookie.parse "key=value; Secure"
18+-- | (Right { name: "key", value: "value", secure: true, ... })
19+-- | ```
20+module PureStack.Cookie
21+ ( module PureStack.Cookie.Generator
22+ , module PureStack.Cookie.Parser
23+ , module PureStack.Cookie.Types
24+ ) where
25+
26+import PureStack.Cookie.Generator (stringify)
27+import PureStack.Cookie.Parser (parse, parseMany)
28+import PureStack.Cookie.Types
29+ ( Cookie
30+ , SameSite(..)
31+ , _domain
32+ , _expires
33+ , _httpOnly
34+ , _maxAge
35+ , _name
36+ , _path
37+ , _secure
38+ , _value
39+ , expire
40+ , fromFields
41+ , getDomain
42+ , getExpires
43+ , getHttpOnly
44+ , getMaxAge
45+ , getName
46+ , getPath
47+ , getSecure
48+ , getValue
49+ , new
50+ , setDomain
51+ , setExpires
52+ , setHttpOnly
53+ , setMaxAge
54+ , setPath
55+ , setSameSite
56+ , setSecure
57+ )
src/PureStack/Cookie/Formatter.pursnew file+65
@@ -0,0 +1,65 @@
1+-- | This module is responsible for formatting and parsing the `Expires`
2+-- | attribute of a `Cookie`.
3+module PureStack.Cookie.Formatter
4+ ( domainTag
5+ , expiresTag
6+ , formatDateTime
7+ , httpOnlyTag
8+ , maxAgeTag
9+ , pathTag
10+ , sameSiteTag
11+ , secureTag
12+ , unformatDateTime
13+ ) where
14+
15+import Data.DateTime (DateTime)
16+import Data.Either (Either)
17+import Data.Formatter.DateTime (Formatter, FormatterCommand(..))
18+import Data.Formatter.DateTime as Formatter
19+import Data.List as List
20+
21+domainTag :: String
22+domainTag = "Domain"
23+
24+expiresTag :: String
25+expiresTag = "Expires"
26+
27+httpOnlyTag :: String
28+httpOnlyTag = "HttpOnly"
29+
30+maxAgeTag :: String
31+maxAgeTag = "Max-Age"
32+
33+pathTag :: String
34+pathTag = "Path"
35+
36+secureTag :: String
37+secureTag = "Secure"
38+
39+sameSiteTag :: String
40+sameSiteTag = "SameSite"
41+
42+expiresFormatter :: Formatter
43+expiresFormatter =
44+ List.fromFoldable
45+ [ DayOfWeekNameShort
46+ , Placeholder ", "
47+ , DayOfMonthTwoDigits
48+ , Placeholder " "
49+ , MonthShort
50+ , Placeholder " "
51+ , YearFull
52+ , Placeholder " "
53+ , Hours24
54+ , Placeholder ":"
55+ , MinutesTwoDigits
56+ , Placeholder ":"
57+ , SecondsTwoDigits
58+ , Placeholder " GMT"
59+ ]
60+
61+formatDateTime :: DateTime -> String
62+formatDateTime = Formatter.format expiresFormatter
63+
64+unformatDateTime :: String -> Either String DateTime
65+unformatDateTime = Formatter.unformat expiresFormatter
src/PureStack/Cookie/Generator.pursnew file+62
@@ -0,0 +1,62 @@
1+-- | This module is responsible for generating the string representation
2+-- | of a `Cookie`.
3+module PureStack.Cookie.Generator
4+ ( stringify
5+ ) where
6+
7+import Prelude
8+import PureStack.Cookie.Formatter
9+ ( domainTag
10+ , expiresTag
11+ , formatDateTime
12+ , httpOnlyTag
13+ , maxAgeTag
14+ , pathTag
15+ , sameSiteTag
16+ , secureTag
17+ )
18+import PureStack.Cookie.Types
19+ ( Cookie
20+ , getDomain
21+ , getExpires
22+ , getHttpOnly
23+ , getMaxAge
24+ , getName
25+ , getPath
26+ , getSameSite
27+ , getSecure
28+ , getValue
29+ )
30+import Data.Array (catMaybes)
31+import Data.Foldable (intercalate)
32+import Data.Maybe (Maybe(..))
33+
34+-- | Return the `String` representation of a `Cookie`.
35+-- |
36+-- | ```purescript
37+-- | > Generator.stringify $ Cookie.setSecure $ Cookie.new "key" "value"
38+-- | key=value; Secure
39+-- | ```
40+stringify :: Cookie -> String
41+stringify cookie =
42+ intercalate "; "
43+ $ catMaybes
44+ [ attr (getName cookie) $ Just (getValue cookie)
45+ , attr domainTag $ getDomain cookie
46+ , attr pathTag $ getPath cookie
47+ , attr expiresTag $ formatDateTime <$> getExpires cookie
48+ , attr maxAgeTag $ show <$> getMaxAge cookie
49+ , attr sameSiteTag $ show <$> getSameSite cookie
50+ , flag secureTag $ getSecure cookie
51+ , flag httpOnlyTag $ getHttpOnly cookie
52+ ]
53+
54+attr :: String -> Maybe String -> Maybe String
55+attr name = case _ of
56+ Nothing -> Nothing
57+ Just value -> Just $ name <> "=" <> value
58+
59+flag :: String -> Boolean -> Maybe String
60+flag name = case _ of
61+ false -> Nothing
62+ true -> Just name
src/PureStack/Cookie/Parser.pursnew file+128
@@ -0,0 +1,128 @@
1+-- | This module is responsible for parsing a string into an
2+-- | `Either ParseError Cookie`.
3+module PureStack.Cookie.Parser
4+ ( parse
5+ , parseMany
6+ ) where
7+
8+import Prelude
9+
10+import Control.Alt ((<|>))
11+import Data.Array as Array
12+import Data.Either (Either(..))
13+import Data.Foldable (class Foldable, foldl)
14+import Data.Int as Int
15+import Data.List (List)
16+import Data.Maybe (Maybe(..))
17+import Data.String as String
18+import Parsing (ParseError, Parser, fail, runParser)
19+import Parsing.Combinators (many, sepBy)
20+import Parsing.String (eof, string)
21+import Parsing.String.Basic (noneOf)
22+import PureStack.Cookie.Formatter (domainTag, expiresTag, httpOnlyTag, maxAgeTag, pathTag, sameSiteTag, secureTag, unformatDateTime)
23+import PureStack.Cookie.Types (Cookie, SameSite(..))
24+import PureStack.Cookie.Types as Cookie
25+
26+type Attribute = Cookie -> Cookie
27+
28+dropSeperator :: Parser String Unit
29+dropSeperator = (string "; " $> unit) <|> (pure unit)
30+
31+asString :: forall f. Functor f => Foldable f => f Char -> String
32+asString = String.fromCodePointArray <<< Array.fromFoldable <<< map String.codePointFromChar
33+
34+stringWithout :: Array Char -> Parser String String
35+stringWithout = map asString <<< many <<< noneOf
36+
37+attributeValue :: Parser String String
38+attributeValue = stringWithout [ ';' ]
39+
40+whitespaceChars :: Array Char
41+whitespaceChars = [ ' ', '\n', '\r', '\t' ]
42+
43+parseDomain :: Parser String Attribute
44+parseDomain = do
45+ domain <- (string $ domainTag <> "=") *> attributeValue
46+ pure $ Cookie.setDomain domain
47+
48+parsePath :: Parser String Attribute
49+parsePath = do
50+ path <- (string $ pathTag <> "=") *> attributeValue
51+ pure $ Cookie.setPath path
52+
53+parseExpires :: Parser String Attribute
54+parseExpires = do
55+ expiresString <- (string $ expiresTag <> "=") *> attributeValue
56+ case unformatDateTime expiresString of
57+ Left e -> fail e
58+ Right expires -> pure $ Cookie.setExpires expires
59+
60+parseMaxAge :: Parser String Attribute
61+parseMaxAge = do
62+ maxAgeString <- (string $ maxAgeTag <> "=") *> attributeValue
63+ case Int.fromString maxAgeString of
64+ Nothing -> fail "invalid integer"
65+ Just maxAge -> pure $ Cookie.setMaxAge maxAge
66+
67+parseSecure :: Parser String Attribute
68+parseSecure = (string secureTag) $> Cookie.setSecure
69+
70+parseHttpOnly :: Parser String Attribute
71+parseHttpOnly = (string httpOnlyTag) $> Cookie.setHttpOnly
72+
73+parseSameSite :: Parser String Attribute
74+parseSameSite = do
75+ sameSiteString <- (string $ sameSiteTag <> "=") *> attributeValue
76+ case sameSiteString of
77+ "Strict" -> pure $ Cookie.setSameSite Strict
78+ "Lax" -> pure $ Cookie.setSameSite Lax
79+ "None" -> pure $ Cookie.setSameSite None
80+ val -> fail $ "unknown SiteSite value " <> val
81+
82+parseAttribute :: Parser String Attribute
83+parseAttribute =
84+ parseDomain
85+ <|> parsePath
86+ <|> parseExpires
87+ <|> parseMaxAge
88+ <|> parseSecure
89+ <|> parseHttpOnly
90+ <|> parseSameSite
91+
92+parseSimpleCookie :: Parser String Cookie
93+parseSimpleCookie = do
94+ name <- stringWithout ([ ';', ',', '=' ] <> whitespaceChars) <* string "="
95+ value <- stringWithout ([ ';', ',' ] <> whitespaceChars)
96+ pure $ Cookie.new name value
97+
98+parseCookie :: Parser String Cookie
99+parseCookie = do
100+ cookie <- parseSimpleCookie <* dropSeperator
101+ attributes <- sepBy parseAttribute (string "; ") <* eof
102+ pure $ foldl (#) cookie attributes
103+
104+parseCookies :: Parser String (List Cookie)
105+parseCookies = sepBy parseSimpleCookie (string "; ") <* eof
106+
107+-- | Parses a `String` into an `Either ParseError Cookie`. This
108+-- | function is primarily intended to parse the `Set-Cookie`
109+-- | header on the client.
110+-- |
111+-- | ```purescript
112+-- | > Parser.parse "key=value; Secure"
113+-- | (Right { name: "key", value: "value", secure: true, ... })
114+-- | ```
115+parse :: String -> Either ParseError Cookie
116+parse = flip runParser parseCookie
117+
118+-- | Parses a `String` into an `Either ParseError (List Cookie)`.
119+-- | HTTP requests can include multiple cookies in a single
120+-- | `Cookie` header consisting of only name/value pairs. This
121+-- | function can be used to parse this header.
122+-- |
123+-- | ```purescript
124+-- | > parseMany "key1=value1; key2=value2"
125+-- | (Right ({ name: "key1", value: "value1", ... } : { name: "key2", value: "value2", ... } : Nil))
126+-- | ```
127+parseMany :: String -> Either ParseError (List Cookie)
128+parseMany = flip runParser parseCookies
src/PureStack/Cookie/Types.pursnew file+189
@@ -0,0 +1,189 @@
1+-- | This module contains the `Cookie` type and functions for operating on data within it.
2+module PureStack.Cookie.Types
3+ ( Cookie
4+ , SameSite(..)
5+ , _domain
6+ , _expires
7+ , _httpOnly
8+ , _maxAge
9+ , _name
10+ , _path
11+ , _secure
12+ , _value
13+ , expire
14+ , fromFields
15+ , getDomain
16+ , getExpires
17+ , getHttpOnly
18+ , getMaxAge
19+ , getName
20+ , getPath
21+ , getSameSite
22+ , getSecure
23+ , getValue
24+ , new
25+ , setDomain
26+ , setExpires
27+ , setHttpOnly
28+ , setMaxAge
29+ , setPath
30+ , setSameSite
31+ , setSecure
32+ ) where
33+
34+import Prelude
35+import Data.DateTime (DateTime)
36+import Data.DateTime as DateTime
37+import Data.Either (Either(..))
38+import Data.Lens (Iso', Lens', iso)
39+import Data.Lens as Lens
40+import Data.Lens.Record (prop)
41+import Data.Maybe (Maybe(..))
42+import Data.Time.Duration (Days(..))
43+import Effect.Now (nowDateTime)
44+import Effect (Effect)
45+import Type.Prelude (Proxy(..))
46+
47+-- | Type representing a `Cookie`'s optional `SameSite` attribute.
48+data SameSite
49+ = Strict
50+ | Lax
51+ | None
52+
53+derive instance eqSameSite :: Eq SameSite
54+
55+derive instance ordSameSite :: Ord SameSite
56+
57+instance showSameSite :: Show SameSite where
58+ show :: SameSite -> String
59+ show Strict = "Strict"
60+ show Lax = "Lax"
61+ show None = "None"
62+
63+type CookieFields =
64+ { name :: String
65+ , value :: String
66+ , domain :: Maybe String
67+ , path :: Maybe String
68+ , expires :: Maybe DateTime
69+ , maxAge :: Maybe Int
70+ , sameSite :: Maybe SameSite
71+ , secure :: Boolean
72+ , httpOnly :: Boolean
73+ }
74+
75+-- | The `Cookie` type
76+newtype Cookie = Cookie CookieFields
77+
78+derive newtype instance eqCookie :: Eq Cookie
79+
80+derive newtype instance ordCookie :: Ord Cookie
81+
82+derive newtype instance showCookie :: Show Cookie
83+
84+-- | The constructor for `Cookie`
85+new :: String -> String -> Cookie
86+new name value =
87+ Cookie
88+ { name
89+ , value
90+ , domain: Nothing
91+ , path: Nothing
92+ , expires: Nothing
93+ , maxAge: Nothing
94+ , sameSite: Nothing
95+ , secure: false
96+ , httpOnly: false
97+ }
98+
99+fromFields :: CookieFields -> Cookie
100+fromFields = Cookie
101+
102+-- | Expire an existing `Cookie`. This sets the `Expires` attribute of
103+-- | the cookie to yesterday's date.
104+expire :: Cookie -> Effect (Either String Cookie)
105+expire cookie = do
106+ now <- nowDateTime
107+ let maybeDate = DateTime.adjust (Days $ -1.0) now
108+
109+ case maybeDate of
110+ Nothing -> pure $ Left "Invalid date"
111+ Just yesterday -> pure $ Right $ setExpires yesterday cookie
112+
113+getDomain :: Cookie -> Maybe String
114+getDomain = Lens.view _domain
115+
116+getExpires :: Cookie -> Maybe DateTime
117+getExpires = Lens.view _expires
118+
119+getHttpOnly :: Cookie -> Boolean
120+getHttpOnly = Lens.view _httpOnly
121+
122+getMaxAge :: Cookie -> Maybe Int
123+getMaxAge = Lens.view _maxAge
124+
125+getName :: Cookie -> String
126+getName = Lens.view _name
127+
128+getPath :: Cookie -> Maybe String
129+getPath = Lens.view _path
130+
131+getSameSite :: Cookie -> Maybe SameSite
132+getSameSite = Lens.view _sameSite
133+
134+getSecure :: Cookie -> Boolean
135+getSecure = Lens.view _secure
136+
137+getValue :: Cookie -> String
138+getValue = Lens.view _value
139+
140+setDomain :: String -> Cookie -> Cookie
141+setDomain = Lens.setJust _domain
142+
143+setExpires :: DateTime -> Cookie -> Cookie
144+setExpires = Lens.setJust _expires
145+
146+setHttpOnly :: Cookie -> Cookie
147+setHttpOnly = Lens.set _httpOnly true
148+
149+setMaxAge :: Int -> Cookie -> Cookie
150+setMaxAge = Lens.setJust _maxAge
151+
152+setPath :: String -> Cookie -> Cookie
153+setPath = Lens.setJust _path
154+
155+setSameSite :: SameSite -> Cookie -> Cookie
156+setSameSite = Lens.setJust _sameSite
157+
158+setSecure :: Cookie -> Cookie
159+setSecure = Lens.set _secure true
160+
161+_Cookie :: Iso' Cookie CookieFields
162+_Cookie = iso (\(Cookie fields) -> fields) Cookie
163+
164+_domain :: Lens' Cookie (Maybe String)
165+_domain = _Cookie <<< prop (Proxy :: Proxy "domain")
166+
167+_expires :: Lens' Cookie (Maybe DateTime)
168+_expires = _Cookie <<< prop (Proxy :: Proxy "expires")
169+
170+_httpOnly :: Lens' Cookie Boolean
171+_httpOnly = _Cookie <<< prop (Proxy :: Proxy "httpOnly")
172+
173+_maxAge :: Lens' Cookie (Maybe Int)
174+_maxAge = _Cookie <<< prop (Proxy :: Proxy "maxAge")
175+
176+_name :: Lens' Cookie String
177+_name = _Cookie <<< prop (Proxy :: Proxy "name")
178+
179+_path :: Lens' Cookie (Maybe String)
180+_path = _Cookie <<< prop (Proxy :: Proxy "path")
181+
182+_sameSite :: Lens' Cookie (Maybe SameSite)
183+_sameSite = _Cookie <<< prop (Proxy :: Proxy "sameSite")
184+
185+_secure :: Lens' Cookie Boolean
186+_secure = _Cookie <<< prop (Proxy :: Proxy "secure")
187+
188+_value :: Lens' Cookie String
189+_value = _Cookie <<< prop (Proxy :: Proxy "value")
src/PureStack/Route.pursnew file+55
@@ -0,0 +1,55 @@
1+module PureStack.Route
2+ ( type (/)
3+ , Slash
4+ , HttpMethod
5+ , class MethodName
6+ , methodName
7+ , GET
8+ , POST
9+ , PUT
10+ , DELETE
11+ , PATCH
12+ , Headers(..)
13+ , Middleware
14+ ) where
15+
16+data HttpMethod
17+
18+-- | The `req` can only ever be `Unit`
19+foreign import data GET :: Type -> Type -> HttpMethod
20+foreign import data POST :: Type -> Type -> HttpMethod
21+foreign import data PUT :: Type -> Type -> HttpMethod
22+foreign import data DELETE :: Type -> Type -> HttpMethod
23+foreign import data PATCH :: Type -> Type -> HttpMethod
24+
25+class MethodName :: HttpMethod -> Constraint
26+class MethodName method where
27+ methodName :: String
28+
29+instance MethodName (GET req resp) where
30+ methodName = "GET"
31+
32+instance MethodName (POST req resp) where
33+ methodName = "POST"
34+
35+instance MethodName (PUT req resp) where
36+ methodName = "PUT"
37+
38+instance MethodName (DELETE req resp) where
39+ methodName = "DELETE"
40+
41+instance MethodName (PATCH req resp) where
42+ methodName = "PATCH"
43+
44+data Slash :: forall k1 k2. k1 -> k2 -> Type
45+data Slash x y
46+
47+-- | Operator for separating path pieces when specisying APIs.
48+-- | `Symbol`s are treated as constant path pieces.
49+-- | `Record`s are treated as query parameters. `Array`s are lists, `Maybe`s are optional query parameters.
50+-- | Other types are captured path pieces.
51+infixr 1 type Slash as /
52+
53+data Headers headers r = Headers headers r
54+
55+data Middleware tag
src/PureStack/Router.pursdeleted−168
@@ -1,168 +0,0 @@
1−module PureStack.Router
2− ( run
3− , type (/)
4− , class RunAPI
5− , runAPI
6− , Slash
7− , GET
8− , POST
9− , PUT
10− , DELETE
11− , PATCH
12− , class ParseRoute
13− , parseRoute
14− , class ToResponse
15− , toResponse
16− ) where
17−
18−import Prelude
19−import Record
20−
21−import Bun.Request (Request)
22−import Bun.Request as Request
23−import Bun.Response (Response)
24−import Bun.Response as Response
25−import Control.Alternative (class Plus, empty)
26−import Data.Argonaut (class EncodeJson, Json, encodeJson)
27−import Data.Array as Array
28−import Data.Maybe (Maybe(..))
29−import Data.Symbol (class IsSymbol, reflectSymbol)
30−import Data.URL (URL)
31−import Data.URL as URL
32−import Data.Unit (Unit)
33−import Effect (Effect)
34−import Effect.Class (liftEffect)
35−import Prim.Row as Row
36−import Prim.RowList (class RowToList)
37−import Prim.RowList as RowList
38−import Record as Record
39−import Type.Prelude (Proxy(..))
40−
41−data GET :: Type -> Type
42−data GET resp
43−
44−data POST :: Type -> Type -> Type
45−data POST req resp
46−
47−data PUT :: Type -> Type -> Type
48−data PUT req resp
49−
50−data DELETE :: Type -> Type -> Type
51−data DELETE req resp
52−
53−data PATCH :: Type -> Type -> Type
54−data PATCH req resp
55−
56−data Slash :: forall k1 k2. k1 -> k2 -> Type
57−data Slash x y
58−
59−infixr 6 type Slash as /
60−
61−type Route = { path :: Array String, verb :: String }
62−
63−routeFromRequest :: Request -> Route
64−routeFromRequest req =
65− { verb: Request.method req
66− , path: case URL.fromString (Request.url req) of
67− Nothing -> []
68− Just url -> case URL.path url of
69− URL.PathEmpty -> []
70− URL.PathAbsolute p -> p
71− URL.PathRelative p -> p
72−
73− }
74−
75−class ParseRoute :: forall k. k -> Type -> Constraint
76−class ParseRoute route handler | route -> handler where
77− parseRoute :: Route -> Maybe (handler -> Request -> Effect Response)
78−
79−instance (ParseRoute rest handler, IsSymbol path) => ParseRoute (path / rest) handler where
80− parseRoute { path, verb } = do
81− { head: h, tail } <- Array.uncons path
82− assert $ h == (reflectSymbol @path Proxy)
83− parseRoute @rest { path: tail, verb: verb }
84−
85−else instance (ParseRoute rest handler, ParsePathPiece t) => ParseRoute (t / rest) (t -> handler) where
86− parseRoute { path, verb } = do
87− { head: h, tail } <- Array.uncons path
88− t <- parsePathPiece @t h
89− parseRoute @rest { path: tail, verb } <#> (\f handler req -> f (handler t) req)
90−
91−else instance ToResponse resp => ParseRoute (GET resp) (Effect resp) where
92− parseRoute { path, verb } = do
93− assert $ verb == "GET"
94− assert $ path == []
95− pure $ \handler _req -> do
96− resp <- handler
97− pure $ toResponse resp { status: 200, statusText: "OK", headers: [] }
98−
99−else instance (ToResponse resp) => ParseRoute (POST Unit resp) (Effect resp) where
100− parseRoute { path, verb } = do
101− assert $ path == []
102− assert $ verb == "POST"
103− pure $ \handler req -> do
104− resp <- handler
105− pure $ toResponse resp { status: 200, statusText: "OK", headers: [] }
106−
107−else instance (FromRequestBody req, ToResponse resp) => ParseRoute (POST req resp) (req -> Effect resp) where
108− parseRoute { path, verb } = do
109− assert $ path == []
110− assert $ verb == "POST"
111− pure $ \handler req -> do
112− fromRequestBody req >>= case _ of
113− Nothing -> pure $ Response.string "" { status: 400, statusText: "Bad Request", headers: [] }
114− Just r -> do
115− resp <- handler r
116− pure $ toResponse resp { status: 200, statusText: "OK", headers: [] }
117−
118−class ToResponse r where
119− toResponse :: r -> Response.Options -> Response
120−
121−instance EncodeJson (Record row) => ToResponse (Record row) where
122− toResponse rec opts = Response.json (encodeJson rec) opts
123−
124−class FromRequestBody r where
125− fromRequestBody :: Request -> Effect (Maybe r)
126−
127−class ParsePathPiece t where
128− parsePathPiece :: String -> Maybe t
129−
130−assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
131−assert true = pure unit
132−assert false = empty
133−
134−class RunAPI row list handlers | row -> handlers where
135− runAPI :: Record handlers -> Route -> Request -> Effect Response
136−
137−instance RunAPI row RowList.Nil () where
138− runAPI _ _ _ = pure notFound
139−
140−instance
141− ( ParseRoute t handler
142− , Row.Cons field t tail row
143− , RunAPI row rest restHandlers
144− , IsSymbol field
145− , Row.Cons field handler restHandlers handlers
146− , Row.Lacks field restHandlers
147− ) =>
148− RunAPI row (RowList.Cons field t rest) handlers where
149− runAPI handlers route@{ path, verb } req =
150− case parseRoute @t { path, verb } of
151− Nothing -> runAPI @row @rest @restHandlers (Record.delete (Proxy @field) handlers) route req
152− Just f -> f (Record.get (Proxy @field) handlers) req
153−
154−notFound :: Response
155−notFound = Response.string "" { status: 404, statusText: "Not Found", headers: [] }
156−
157−internalServerError :: Response
158−internalServerError = Response.string "" { status: 500, statusText: "Internal Server Error", headers: [] }
159−
160−run
161− :: forall @row list handlers
162− . RowToList row list
163− => RunAPI row list handlers
164− => Record handlers
165− -> Request
166− -> Effect Response
167−run handlers req = runAPI @row @list @handlers handlers (routeFromRequest req) req
168−
src/PureStack/Server.pursnew file+410
@@ -0,0 +1,410 @@
1+module PureStack.Server
2+ ( Server
3+ , Response(..)
4+ , ResponseBody(..)
5+ , class ToResponse
6+ , toResponse
7+ , class FromRequest
8+ , fromRequest
9+ , run
10+ , notFound
11+ , internalServerError
12+ , badRequest
13+ -------------------------------------------------------------------------------------------------
14+ , class ServeAPI
15+ , serveAPI
16+ , Nt(..)
17+ , class ServeRoute
18+ , serveRoute
19+ , class ServeQuery
20+ , serveQuery
21+ , class ParsePathPiece
22+ , parsePathPiece
23+ , class FromHeaders
24+ , fromHeaders
25+ , class FromHeader
26+ , fromHeader
27+ ) where
28+
29+import Prelude
30+import PureStack.Route
31+
32+import Bun.Request (Request)
33+import Bun.Request as Bun
34+import Bun.Request as Request
35+import Bun.Response as Bun.Response
36+import Control.Alternative (class Plus, empty)
37+import Control.Monad.Except (ExceptT, runExceptT)
38+import Data.Argonaut (class DecodeJson, class EncodeJson, Json, decodeJson, encodeJson)
39+import Data.Array as Array
40+import Data.Either (Either(..), either)
41+import Data.Int as Int
42+import Data.Map (Map)
43+import Data.Map as Map
44+import Data.Maybe (Maybe(..))
45+import Data.Number as Number
46+import Data.String as String
47+import Data.Symbol (class IsSymbol, reflectSymbol)
48+import Data.Traversable (traverse)
49+import Data.URL as URL
50+import Effect.Aff (Aff)
51+import Effect.Aff.Class (liftAff)
52+import Foreign.Object (Object)
53+import Foreign.Object as Object
54+import Prim.Row as Row
55+import Prim.RowList (class RowToList, RowList)
56+import Prim.RowList as RowList
57+import PureStack.Cookie (Cookie)
58+import PureStack.Cookie as Cookie
59+import Record as Record
60+import Record.Builder (Builder)
61+import Record.Builder as Builder
62+import Type.Proxy (Proxy(..))
63+
64+type Route =
65+ { path :: Array String
66+ , verb :: String
67+ , query :: Map String (Array String)
68+ , originalRequest :: Bun.Request
69+ }
70+
71+routeFromRequest :: Request -> Route
72+routeFromRequest req =
73+ let
74+ url = URL.fromString $ Request.url req
75+ in
76+ { verb: Request.method req
77+ , path: case url of
78+ Nothing -> []
79+ Just u -> case URL.path u of
80+ URL.PathEmpty -> []
81+ URL.PathAbsolute p -> p
82+ URL.PathRelative p -> p
83+ , query: case url of
84+ Nothing -> Map.empty
85+ Just u -> URL.query u
86+ , originalRequest: req
87+ }
88+
89+type Server a = ExceptT Response Aff a
90+
91+newtype Nt m = Nt (forall x. m x -> Server x)
92+
93+class ServeRoute :: forall k. k -> Type -> Row Type -> (Type -> Type) -> Constraint
94+class ServeRoute route handler middlewares m | route middlewares -> handler, handler -> m where
95+ serveRoute :: Route -> Maybe (Nt m -> handler -> Record middlewares -> Request -> Server Response)
96+
97+instance (ServeRoute rest handler middlewares m, IsSymbol path) => ServeRoute (path / rest) handler middlewares m where
98+ serveRoute route@{ path } = do
99+ { head: h, tail } <- Array.uncons path
100+ assert $ h == (reflectSymbol @path Proxy)
101+ serveRoute @rest route { path = tail }
102+
103+else instance
104+ ( ServeRoute rest handler middlewares m
105+ , ServeQuery row list
106+ , RowToList row list
107+ ) =>
108+ ServeRoute (Record row / rest) (Record row -> handler) middlewares m where
109+ serveRoute route@{ query } = do
110+ builder <- serveQuery @row @list query
111+ serveRoute @rest route <#> (\f nt handler req -> f nt (handler $ Builder.buildFromScratch builder) req)
112+
113+else instance
114+ ( ServeRoute rest handler middlewares m
115+ , IsSymbol field
116+ , Row.Cons field (Bun.Request -> m x) tail middlewares
117+ ) =>
118+ ServeRoute (Middleware field / rest) (x -> handler) middlewares m where
119+ serveRoute route@{ originalRequest } = do
120+ serveRoute @rest route <#>
121+ ( \f nt@(Nt ntf) handler middlewares req -> do
122+ x <- ntf $ Record.get (Proxy @field) middlewares originalRequest
123+ f nt (handler x) middlewares req
124+ )
125+
126+else instance (ServeRoute rest handler middlewares m, ParsePathPiece t) => ServeRoute (t / rest) (t -> handler) middlewares m where
127+ serveRoute route@{ path } = do
128+ { head: h, tail } <- Array.uncons path
129+ t <- parsePathPiece @t h
130+ serveRoute @rest route { path = tail } <#> (\f nt handler mws req -> f nt (handler t) mws req)
131+
132+else instance MethodName (meth Unit Unit) => ServeRoute (meth Unit Unit) (m Unit) middlewares m where
133+ serveRoute { path, verb } = do
134+ assert $ verb == methodName @(meth Unit Unit)
135+ assert $ path == []
136+ pure $ \(Nt nt) handler _middlewares _req -> do
137+ nt handler
138+ pure $ ok
139+else instance (ToResponse resp, MethodName (meth Unit resp)) => ServeRoute (meth Unit resp) (m resp) middlewares m where
140+ serveRoute { path, verb } = do
141+ assert $ verb == methodName @(meth Unit resp)
142+ assert $ path == []
143+ pure $ \(Nt nt) handler _middlewares _req -> do
144+ resp <- nt handler
145+ pure $ toResponse resp
146+else instance (FromRequest req, MethodName (meth resp Unit)) => ServeRoute (meth resp Unit) (req -> m Unit) middlewares m where
147+ serveRoute { path, verb } = do
148+ assert $ verb == methodName @(meth resp Unit)
149+ assert $ path == []
150+ pure $ \(Nt nt) handler _middlewares req -> do
151+ liftAff (fromRequest req) >>= case _ of
152+ Nothing -> pure $ badRequest
153+ Just r -> do
154+ nt $ handler r
155+ pure $ ok
156+else instance (FromRequest req, ToResponse resp, MethodName (meth req resp)) => ServeRoute (meth req resp) (req -> m resp) middlewares m where
157+ serveRoute { path, verb } = do
158+ assert $ verb == methodName @(meth req resp)
159+ assert $ path == []
160+ pure $ \(Nt nt) handler _middlewares req -> do
161+ liftAff (fromRequest req) >>= case _ of
162+ Nothing -> pure $ badRequest
163+ Just r -> do
164+ resp <- nt $ handler r
165+ pure $ toResponse resp
166+
167+else instance
168+ ( ServeAPI routes list handlers middlewares m
169+ , RowToList routes list
170+ ) =>
171+ ServeRoute routes (Record handlers) middlewares m where
172+ serveRoute route = pure $ \nt handlers middlewares req ->
173+ serveAPI @routes @list nt handlers middlewares route req
174+
175+class ServeQuery :: Row Type -> RowList Type -> Constraint
176+class ServeQuery row list | list -> row where
177+ serveQuery :: Map String (Array String) -> Maybe (Builder (Record ()) (Record row))
178+
179+instance ServeQuery () RowList.Nil where
180+ serveQuery _ = Just $ identity
181+
182+instance
183+ ( ParsePathPiece t
184+ , ServeQuery tail rest
185+ , IsSymbol field
186+ , Row.Cons field (Array t) tail row
187+ , Row.Lacks field tail
188+ ) =>
189+ ServeQuery row (RowList.Cons field (Array t) rest) where
190+ serveQuery m = do
191+ r <- serveQuery @tail @rest m
192+ case Map.lookup (reflectSymbol @field Proxy) m of
193+ Nothing -> pure $ Builder.insert (Proxy @field) [] <<< r
194+ Just a -> do
195+ v <- parsePathPiece @t `traverse` a
196+ pure $ Builder.insert (Proxy @field) v <<< r
197+
198+else instance
199+ ( ParsePathPiece t
200+ , ServeQuery tail rest
201+ , IsSymbol field
202+ , Row.Cons field (Maybe t) tail row
203+ , Row.Lacks field tail
204+ ) =>
205+ ServeQuery row (RowList.Cons field (Maybe t) rest) where
206+ serveQuery m = do
207+ r <- serveQuery @tail @rest m
208+ case Map.lookup (reflectSymbol @field Proxy) m of
209+ Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
210+ Just a -> do
211+ { head, tail } <- Array.uncons a
212+ assert $ tail == []
213+ v <- parsePathPiece @t head
214+ pure $ Builder.insert (Proxy @field) (Just v) <<< r
215+
216+else instance
217+ ( ParsePathPiece t
218+ , ServeQuery tail rest
219+ , IsSymbol field
220+ , Row.Cons field t tail row
221+ , Row.Lacks field tail
222+ ) =>
223+ ServeQuery row (RowList.Cons field t rest) where
224+ serveQuery m = do
225+ { head, tail } <- Map.lookup (reflectSymbol @field Proxy) m >>= Array.uncons
226+ assert $ tail == []
227+ v <- parsePathPiece @t head
228+ r <- serveQuery @tail @rest m
229+ pure $ Builder.insert (Proxy @field) v <<< r
230+
231+data ResponseBody
232+ = JsonResponseBody Json
233+ | EmptyResponseBody
234+ | StringResponseBody String
235+
236+newtype Response = Response { body :: ResponseBody, status :: Int, statusText :: String, headers :: Object String }
237+
238+class ToResponse r where
239+ toResponse :: r -> Response
240+
241+instance EncodeJson (Record row) => ToResponse (Record row) where
242+ toResponse rec = Response { body: JsonResponseBody $ encodeJson rec, status: 200, statusText: "OK", headers: Object.empty }
243+
244+instance ToResponse Response where
245+ toResponse resp = resp
246+
247+class FromRequest r where
248+ fromRequest :: Request -> Aff (Maybe r)
249+
250+instance DecodeJson (Record row) => FromRequest (Record row) where
251+ fromRequest req = Request.json req <#> \j -> case decodeJson j of
252+ Right r -> Just r
253+ Left _ -> Nothing
254+
255+instance FromRequest Request where
256+ fromRequest req = pure $ Just req
257+
258+instance FromRequest Unit where
259+ fromRequest _ = pure $ Just unit
260+
261+instance
262+ ( FromRequest req
263+ , FromHeader header
264+ ) =>
265+ FromRequest (Headers (Object header) req) where
266+ fromRequest req = do
267+ r <- fromRequest req
268+ pure $ do
269+ headers <- traverse fromHeader (Request.headers req)
270+ r' <- r
271+ pure $ Headers headers r'
272+
273+instance
274+ ( RowToList headers headersList
275+ , FromRequest req
276+ , FromHeaders headers headersList
277+ ) =>
278+ FromRequest (Headers (Record headers) req) where
279+ fromRequest req = do
280+ r <- fromRequest req
281+ pure $ do
282+ headers <- fromHeaders @headers @headersList (Request.headers req)
283+ r' <- r
284+ pure $ Headers (Builder.buildFromScratch headers) r'
285+
286+class FromHeader t where
287+ fromHeader :: String -> Maybe t
288+
289+instance FromHeader Cookie where
290+ fromHeader = Cookie.parse >>> case _ of
291+ Left _ -> Nothing
292+ Right x -> Just x
293+
294+instance FromHeader String where
295+ fromHeader = Just
296+
297+instance FromHeader Int where
298+ fromHeader = Int.fromString
299+
300+class FromHeaders :: Row Type -> RowList Type -> Constraint
301+class FromHeaders row list | list -> row where
302+ fromHeaders :: Object String -> Maybe (Builder {} (Record row))
303+
304+instance FromHeaders () RowList.Nil where
305+ fromHeaders _ = Just $ identity
306+
307+instance
308+ ( FromHeader t
309+ , IsSymbol field
310+ , FromHeaders tail rest
311+ , Row.Cons field (Maybe t) tail row
312+ , Row.Lacks field tail
313+ ) =>
314+ FromHeaders row (RowList.Cons field (Maybe t) rest) where
315+ fromHeaders headers = do
316+ r <- fromHeaders @tail @rest headers
317+ case Object.lookup (reflectSymbol @field Proxy) headers of
318+ Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
319+ Just t -> do
320+ v <- fromHeader t
321+ pure $ Builder.insert (Proxy @field) (Just v) <<< r
322+
323+else instance
324+ ( FromHeader t
325+ , IsSymbol field
326+ , FromHeaders tail rest
327+ , Row.Cons field t tail row
328+ , Row.Lacks field tail
329+ ) =>
330+ FromHeaders row (RowList.Cons field t rest) where
331+ fromHeaders headers = do
332+ t <- Object.lookup (reflectSymbol @field Proxy) headers >>= fromHeader
333+ r <- fromHeaders @tail @rest headers
334+ pure $ Builder.insert (Proxy @field) t <<< r
335+
336+class ParsePathPiece t where
337+ parsePathPiece :: String -> Maybe t
338+
339+instance ParsePathPiece String where
340+ parsePathPiece = Just
341+
342+instance ParsePathPiece Int where
343+ parsePathPiece = Int.fromString
344+
345+instance ParsePathPiece Number where
346+ parsePathPiece = Number.fromString
347+
348+instance ParsePathPiece Boolean where
349+ parsePathPiece s = case String.toLower s of
350+ "true" -> Just true
351+ "1" -> Just true
352+ "false" -> Just false
353+ "0" -> Just false
354+ _ -> Nothing
355+
356+assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
357+assert true = pure unit
358+assert false = empty
359+
360+class ServeAPI :: Row Type -> RowList Type -> Row Type -> Row Type -> (Type -> Type) -> Constraint
361+class ServeAPI row list handlers middlewares m | row -> handlers, handlers -> m where
362+ serveAPI :: Nt m -> Record handlers -> Record middlewares -> Route -> Request -> Server Response
363+
364+instance ServeAPI row RowList.Nil () middlewares m where
365+ serveAPI _ _ _ _ _ = pure notFound
366+
367+instance
368+ ( ServeRoute t handler middlewares m
369+ , Row.Cons field t tail row
370+ , ServeAPI row rest restHandlers middlewares m
371+ , IsSymbol field
372+ , Row.Cons field handler restHandlers handlers
373+ , Row.Lacks field restHandlers
374+ ) =>
375+ ServeAPI row (RowList.Cons field t rest) handlers middlewares m where
376+ serveAPI nt handlers middlewares route req =
377+ case serveRoute @t route of
378+ Nothing -> serveAPI @row @rest @restHandlers @middlewares nt (Record.delete (Proxy @field) handlers) middlewares route req
379+ Just f -> f nt (Record.get (Proxy @field) handlers) middlewares req
380+
381+notFound :: Response
382+notFound = Response { body: EmptyResponseBody, status: 404, statusText: "Not Found", headers: Object.empty }
383+
384+internalServerError :: Response
385+internalServerError = Response { body: EmptyResponseBody, status: 500, statusText: "Internal Server Error", headers: Object.empty }
386+
387+badRequest :: Response
388+badRequest = Response { body: EmptyResponseBody, status: 400, statusText: "Bad Request", headers: Object.empty }
389+
390+ok :: Response
391+ok = Response { body: EmptyResponseBody, status: 200, statusText: "OK", headers: Object.empty }
392+
393+-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
394+-- | instead you can just pass `identity` as the first argument.
395+run
396+ :: forall @row list handlers m middlewares
397+ . RowToList row list
398+ => ServeAPI row list handlers middlewares m
399+ => (forall x. m x -> Server x)
400+ -> { handlers :: Record handlers, middlewares :: Record middlewares }
401+ -> Request
402+ -> Aff Bun.Response.Response
403+run nt { handlers, middlewares } req = do
404+ Response { body, headers, status, statusText } <-
405+ map (either identity identity) $ runExceptT $ serveAPI @row @list @handlers (Nt nt) handlers middlewares (routeFromRequest req) req
406+ let opts = { headers, status, statusText }
407+ pure $ case body of
408+ EmptyResponseBody -> Bun.Response.string "" opts
409+ JsonResponseBody json -> Bun.Response.json json opts
410+ StringResponseBody string -> Bun.Response.string string opts