@typed/router 0.31.0 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +111 -2
  2. package/dist/AST.d.ts +96 -0
  3. package/dist/AST.d.ts.map +1 -0
  4. package/dist/AST.js +32 -0
  5. package/dist/CurrentRoute.d.ts +18 -0
  6. package/dist/CurrentRoute.d.ts.map +1 -0
  7. package/dist/CurrentRoute.js +18 -0
  8. package/dist/Matcher.d.ts +191 -0
  9. package/dist/Matcher.d.ts.map +1 -0
  10. package/dist/Matcher.js +597 -0
  11. package/dist/Parser.d.ts +96 -0
  12. package/dist/Parser.d.ts.map +1 -0
  13. package/dist/Parser.js +1 -0
  14. package/dist/Path.d.ts +216 -0
  15. package/dist/Path.d.ts.map +1 -0
  16. package/dist/Path.js +248 -0
  17. package/dist/Route.d.ts +57 -0
  18. package/dist/Route.d.ts.map +1 -0
  19. package/dist/Route.js +151 -0
  20. package/dist/Router.d.ts +9 -0
  21. package/dist/Router.d.ts.map +1 -0
  22. package/dist/Router.js +8 -0
  23. package/dist/Uri.d.ts +115 -0
  24. package/dist/Uri.d.ts.map +1 -0
  25. package/dist/Uri.js +1 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +4 -0
  29. package/package.json +25 -69
  30. package/src/AST.ts +166 -0
  31. package/src/CurrentRoute.ts +30 -331
  32. package/src/Matcher.test.ts +476 -0
  33. package/src/Matcher.ts +1269 -328
  34. package/src/Parser.ts +282 -0
  35. package/src/Path.test.ts +318 -0
  36. package/src/Path.ts +691 -0
  37. package/src/Route.test.ts +268 -0
  38. package/src/Route.ts +316 -0
  39. package/src/Router.ts +31 -0
  40. package/src/Uri.ts +214 -0
  41. package/src/index.ts +4 -28
  42. package/tsconfig.json +6 -0
  43. package/CurrentRoute/package.json +0 -6
  44. package/LICENSE +0 -21
  45. package/MatchInput/package.json +0 -6
  46. package/Matcher/package.json +0 -6
  47. package/RouteGuard/package.json +0 -6
  48. package/RouteMatch/package.json +0 -6
  49. package/dist/cjs/CurrentRoute.js +0 -170
  50. package/dist/cjs/CurrentRoute.js.map +0 -1
  51. package/dist/cjs/MatchInput.js +0 -96
  52. package/dist/cjs/MatchInput.js.map +0 -1
  53. package/dist/cjs/Matcher.js +0 -138
  54. package/dist/cjs/Matcher.js.map +0 -1
  55. package/dist/cjs/RouteGuard.js +0 -78
  56. package/dist/cjs/RouteGuard.js.map +0 -1
  57. package/dist/cjs/RouteMatch.js +0 -49
  58. package/dist/cjs/RouteMatch.js.map +0 -1
  59. package/dist/cjs/index.js +0 -53
  60. package/dist/cjs/index.js.map +0 -1
  61. package/dist/dts/CurrentRoute.d.ts +0 -94
  62. package/dist/dts/CurrentRoute.d.ts.map +0 -1
  63. package/dist/dts/MatchInput.d.ts +0 -135
  64. package/dist/dts/MatchInput.d.ts.map +0 -1
  65. package/dist/dts/Matcher.d.ts +0 -121
  66. package/dist/dts/Matcher.d.ts.map +0 -1
  67. package/dist/dts/RouteGuard.d.ts +0 -94
  68. package/dist/dts/RouteGuard.d.ts.map +0 -1
  69. package/dist/dts/RouteMatch.d.ts +0 -50
  70. package/dist/dts/RouteMatch.d.ts.map +0 -1
  71. package/dist/dts/index.d.ts +0 -24
  72. package/dist/dts/index.d.ts.map +0 -1
  73. package/dist/esm/CurrentRoute.js +0 -152
  74. package/dist/esm/CurrentRoute.js.map +0 -1
  75. package/dist/esm/MatchInput.js +0 -79
  76. package/dist/esm/MatchInput.js.map +0 -1
  77. package/dist/esm/Matcher.js +0 -130
  78. package/dist/esm/Matcher.js.map +0 -1
  79. package/dist/esm/RouteGuard.js +0 -57
  80. package/dist/esm/RouteGuard.js.map +0 -1
  81. package/dist/esm/RouteMatch.js +0 -29
  82. package/dist/esm/RouteMatch.js.map +0 -1
  83. package/dist/esm/index.js +0 -24
  84. package/dist/esm/index.js.map +0 -1
  85. package/dist/esm/package.json +0 -4
  86. package/src/MatchInput.ts +0 -282
  87. package/src/RouteGuard.ts +0 -217
  88. package/src/RouteMatch.ts +0 -104
@@ -0,0 +1,597 @@
1
+ import * as findMyWay from "find-my-way-ts";
2
+ import * as Cause from "effect/Cause";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Exit from "effect/Exit";
5
+ import { interrupt, isSuccess } from "effect/Exit";
6
+ import { dual, identity } from "effect/Function";
7
+ import * as Result from "effect/Result";
8
+ import * as Layer from "effect/Layer";
9
+ import * as Option from "effect/Option";
10
+ import { pipeArguments } from "effect/Pipeable";
11
+ import * as Schema from "effect/Schema";
12
+ import { makeFormatterDefault } from "effect/SchemaIssue";
13
+ import * as Scope from "effect/Scope";
14
+ import * as ServiceMap from "effect/ServiceMap";
15
+ import * as Stream from "effect/Stream";
16
+ import { exit } from "@typed/fx/Fx";
17
+ import { mapEffect } from "@typed/fx/Fx/combinators/mapEffect";
18
+ import { provideServices } from "@typed/fx/Fx/combinators/provide";
19
+ import { skipRepeats } from "@typed/fx/Fx/combinators/skipRepeats";
20
+ import { switchMap } from "@typed/fx/Fx/combinators/switchMap";
21
+ import { unwrap } from "@typed/fx/Fx/combinators/unwrap";
22
+ import { fromEffect, never } from "@typed/fx/Fx/constructors/fromEffect";
23
+ import { succeed } from "@typed/fx/Fx/constructors/succeed";
24
+ import { fromStream } from "@typed/fx/Fx/stream";
25
+ import { isFx } from "@typed/fx/Fx/TypeId";
26
+ import { RefSubject } from "@typed/fx/RefSubject";
27
+ import { CurrentPath, Navigation } from "@typed/navigation/Navigation";
28
+ import * as AST from "./AST.js";
29
+ import { CurrentRoute } from "./CurrentRoute.js";
30
+ import { Join, make as makeRoute } from "./Route.js";
31
+ function isMatchHandlerFn(handler) {
32
+ return typeof handler === "function";
33
+ }
34
+ function isHandlerOptions(value) {
35
+ return typeof value === "object" && value !== null && "handler" in value;
36
+ }
37
+ function parseMatchArgs(args) {
38
+ const [first, second, third] = args;
39
+ // Single arg: full options object (Overload 9)
40
+ if (second === undefined) {
41
+ const opts = first;
42
+ return {
43
+ route: opts.route,
44
+ handler: opts.handler,
45
+ guard: undefined,
46
+ layout: opts.layout,
47
+ catchFn: opts.catch,
48
+ dependencies: opts.dependencies,
49
+ };
50
+ }
51
+ // Two args
52
+ if (third === undefined) {
53
+ if (isHandlerOptions(second)) {
54
+ // Overload 3: match(route, options)
55
+ const opts = second;
56
+ return {
57
+ route: first,
58
+ handler: opts.handler,
59
+ guard: undefined,
60
+ layout: opts.layout,
61
+ catchFn: opts.catch,
62
+ dependencies: opts.dependencies,
63
+ };
64
+ }
65
+ // Overloads 1, 2, 4: match(route, handler)
66
+ return {
67
+ route: first,
68
+ handler: second,
69
+ guard: undefined,
70
+ layout: undefined,
71
+ catchFn: undefined,
72
+ dependencies: undefined,
73
+ };
74
+ }
75
+ // Three args
76
+ if (isHandlerOptions(third)) {
77
+ // Overload 7: match(route, guard, options)
78
+ const opts = third;
79
+ return {
80
+ route: first,
81
+ handler: opts.handler,
82
+ guard: second,
83
+ layout: opts.layout,
84
+ catchFn: opts.catch,
85
+ dependencies: opts.dependencies,
86
+ };
87
+ }
88
+ // Overloads 5, 6, 8: match(route, guard, handler)
89
+ return {
90
+ route: first,
91
+ handler: third,
92
+ guard: second,
93
+ layout: undefined,
94
+ catchFn: undefined,
95
+ dependencies: undefined,
96
+ };
97
+ }
98
+ class MatcherImpl {
99
+ cases;
100
+ constructor(cases) {
101
+ this.cases = cases;
102
+ this.match = this.match.bind(this);
103
+ this.catch = this.catch.bind(this);
104
+ this.catchTag = this.catchTag.bind(this);
105
+ this.layout = this.layout.bind(this);
106
+ this.provide = this.provide.bind(this);
107
+ this.provideService = this.provideService.bind(this);
108
+ }
109
+ match(...args) {
110
+ const parsed = parseMatchArgs(args);
111
+ const normalizedGuard = parsed.guard !== undefined
112
+ ? getGuard(parsed.guard)
113
+ : defaultGuard();
114
+ const routeAst = AST.route(parsed.route.ast, parsed.handler, normalizedGuard);
115
+ let matches = [routeAst];
116
+ if (parsed.layout !== undefined) {
117
+ matches = [AST.layout(matches, parsed.layout)];
118
+ }
119
+ if (parsed.catchFn !== undefined) {
120
+ matches = [AST.catchCause(matches, parsed.catchFn)];
121
+ }
122
+ if (parsed.dependencies !== undefined && parsed.dependencies.length > 0) {
123
+ matches = [AST.layer(matches, normalizeDependencies(parsed.dependencies))];
124
+ }
125
+ return new MatcherImpl([...this.cases, ...matches]);
126
+ }
127
+ prefix(route) {
128
+ return new MatcherImpl([AST.prefixed(this.cases, route.ast)]);
129
+ }
130
+ provide(...layers) {
131
+ return new MatcherImpl([AST.layer(this.cases, layers)]);
132
+ }
133
+ provideService(tag, service) {
134
+ return this.provideServices(ServiceMap.make(tag, service));
135
+ }
136
+ provideServices(services) {
137
+ return this.provide(Layer.succeedServices(services));
138
+ }
139
+ catchCause(f) {
140
+ return new MatcherImpl([AST.catchCause(this.cases, f)]);
141
+ }
142
+ catch(f) {
143
+ return this.catchCause((causeRef) => unwrap(Effect.gen(function* () {
144
+ const cause = yield* causeRef;
145
+ const result = Cause.findFail(cause);
146
+ if (Result.isFailure(result)) {
147
+ return fromEffect(Effect.failCause(result.failure));
148
+ }
149
+ return f(result.success.error);
150
+ })));
151
+ }
152
+ catchTag(tag, f) {
153
+ const rethrow = (cause) => fromEffect(Effect.failCause(cause));
154
+ return new MatcherImpl([
155
+ AST.catchCause(this.cases, (causeRef) => unwrap(Effect.gen(function* () {
156
+ const cause = yield* causeRef;
157
+ const result = Cause.findFail(cause);
158
+ if (Result.isFailure(result)) {
159
+ return rethrow(cause);
160
+ }
161
+ if (matchesTag(tag, result.success.error)) {
162
+ return f(result.success.error);
163
+ }
164
+ return rethrow(cause);
165
+ }))),
166
+ ]);
167
+ }
168
+ layout(layout) {
169
+ return new MatcherImpl([
170
+ AST.layout(this.cases, layout),
171
+ ]);
172
+ }
173
+ pipe() {
174
+ return pipeArguments(this, arguments);
175
+ }
176
+ }
177
+ function normalizeHandler(handler) {
178
+ if (isMatchHandlerFn(handler))
179
+ return (params) => toFx(handler(params));
180
+ return () => toFx(handler);
181
+ }
182
+ function toFx(value) {
183
+ if (isFx(value))
184
+ return value;
185
+ if (Stream.isStream(value))
186
+ return fromStream(value);
187
+ if (Effect.isEffect(value))
188
+ return fromEffect(value);
189
+ return succeed(value);
190
+ }
191
+ export const empty = new MatcherImpl([]);
192
+ export const match = empty.match.bind(empty);
193
+ export class RouteGuardError extends Schema.ErrorClass("@typed/router/RouteGuardError")({
194
+ _tag: Schema.tag("RouteGuardError"),
195
+ path: Schema.String,
196
+ causes: Schema.Array(Schema.Unknown),
197
+ }) {
198
+ }
199
+ export class RouteNotFound extends Schema.ErrorClass("@typed/router/RouteNotFound")({
200
+ _tag: Schema.tag("RouteNotFound"),
201
+ path: Schema.String,
202
+ }) {
203
+ }
204
+ export class RouteDecodeError extends Schema.ErrorClass("@typed/router/RouteDecodeError")({
205
+ _tag: Schema.tag("RouteDecodeError"),
206
+ path: Schema.String,
207
+ cause: Schema.String,
208
+ }) {
209
+ }
210
+ export function run(matcher) {
211
+ return unwrap(Effect.gen(function* () {
212
+ const fiberId = yield* Effect.fiberId;
213
+ const rootScope = yield* Effect.scope;
214
+ const current = yield* CurrentRoute;
215
+ const prefixed = matcher.prefix(current.route);
216
+ const entries = compile(prefixed.cases);
217
+ const router = findMyWay.make({
218
+ ignoreTrailingSlash: true,
219
+ caseSensitive: false,
220
+ });
221
+ const handlersByPath = new Map();
222
+ const memoMap = yield* Layer.makeMemoMap;
223
+ const layerManager = makeLayerManager(memoMap, rootScope, fiberId);
224
+ const layoutManager = makeLayoutManager(rootScope, fiberId);
225
+ const catchManager = makeCatchManager(rootScope, fiberId);
226
+ for (const entry of entries) {
227
+ const path = entry.route.path;
228
+ const existing = handlersByPath.get(path);
229
+ if (existing !== undefined) {
230
+ existing.push(entry);
231
+ }
232
+ else {
233
+ const list = [entry];
234
+ handlersByPath.set(path, list);
235
+ router.all(path, list);
236
+ }
237
+ }
238
+ let currentState = null;
239
+ return CurrentPath.pipe(mapEffect(Effect.fn(function* (path) {
240
+ const result = router.find("GET", path);
241
+ if (result === undefined)
242
+ return yield* new RouteNotFound({ path });
243
+ const input = { ...result.params, ...result.searchParams };
244
+ const entries = result.handler;
245
+ const guardCauses = [];
246
+ let matchedEntry = undefined;
247
+ let matchedParams = undefined;
248
+ let matchedPrepared = undefined;
249
+ for (const entry of entries) {
250
+ const params = yield* Effect.mapErrorEager(entry.decode(input), (cause) => new RouteDecodeError({ path, cause: makeFormatterDefault()(cause.issue) }));
251
+ const prepared = yield* layerManager.prepare(entry.layers);
252
+ const guardExit = yield* entry
253
+ .guard(params)
254
+ .pipe(Effect.provideServices(prepared.services), Effect.exit);
255
+ if (Exit.isFailure(guardExit)) {
256
+ guardCauses.push(guardExit.cause);
257
+ yield* prepared.rollback;
258
+ continue;
259
+ }
260
+ if (Option.isNone(guardExit.value)) {
261
+ yield* prepared.rollback;
262
+ continue;
263
+ }
264
+ matchedEntry = entry;
265
+ matchedParams = guardExit.value.value;
266
+ matchedPrepared = prepared;
267
+ break;
268
+ }
269
+ if (matchedEntry === undefined || matchedPrepared === undefined) {
270
+ return yield* new RouteGuardError({ path, causes: guardCauses });
271
+ }
272
+ yield* matchedPrepared.commit;
273
+ if (currentState !== null && currentState.entry === matchedEntry) {
274
+ yield* RefSubject.set(currentState.params, matchedParams);
275
+ yield* layoutManager.updateParams(matchedEntry.layouts, matchedParams);
276
+ return currentState.fx;
277
+ }
278
+ if (currentState !== null) {
279
+ yield* Scope.close(currentState.scope, interrupt(fiberId));
280
+ currentState = null;
281
+ }
282
+ const scope = yield* Scope.fork(rootScope);
283
+ const paramsRef = yield* RefSubject.make(matchedParams).pipe(Scope.provide(scope));
284
+ const preparedServices = matchedPrepared.services;
285
+ const handlerServices = ServiceMap.merge(preparedServices, ServiceMap.make(Scope.Scope, scope));
286
+ const handlerFx = matchedEntry
287
+ .handler(paramsRef)
288
+ .pipe(provideServices(handlerServices));
289
+ const withLayouts = yield* layoutManager.apply(matchedEntry.layouts, matchedParams, handlerFx, preparedServices);
290
+ const withCatches = yield* catchManager.apply(matchedEntry.catches, withLayouts, preparedServices);
291
+ const fx = withCatches;
292
+ currentState = {
293
+ entry: matchedEntry,
294
+ params: paramsRef,
295
+ scope,
296
+ fx,
297
+ };
298
+ return currentState.fx;
299
+ })), skipRepeats, switchMap(identity));
300
+ }));
301
+ }
302
+ export const catchCause = dual(2, (input, f) => {
303
+ const eff = Effect.gen(function* () {
304
+ const fiberId = yield* Effect.fiberId;
305
+ const rootScope = yield* Effect.scope;
306
+ const fx = isFx(input) ? input : run(input);
307
+ const manager = makeCatchManager(rootScope, fiberId);
308
+ const result = yield* manager.apply([f], fx, ServiceMap.empty());
309
+ return result;
310
+ });
311
+ return unwrap(eff);
312
+ });
313
+ export const catch_ = dual(2, (input, f) => catchCause(input, (causeRef) => unwrap(Effect.gen(function* () {
314
+ const cause = yield* causeRef;
315
+ const result = Cause.findFail(cause);
316
+ if (Result.isFailure(result)) {
317
+ return fromEffect(Effect.failCause(result.failure));
318
+ }
319
+ return f(result.success.error);
320
+ }))));
321
+ export { catch_ as catch };
322
+ export const catchTag = dual(3, (input, k, f) => catchCause(input, (causeRef) => unwrap(Effect.gen(function* () {
323
+ const cause = yield* causeRef;
324
+ const result = Cause.findFail(cause);
325
+ if (Result.isFailure(result)) {
326
+ return fromEffect(Effect.failCause(result.failure));
327
+ }
328
+ if (matchesTag(k, result.success.error)) {
329
+ return f(result.success.error);
330
+ }
331
+ return fromEffect(Effect.fail(result.success.error));
332
+ }))));
333
+ export const redirectTo = (path) => (input) => catchCause(input, (_) => Navigation.navigate(path).pipe(Effect.matchCause({
334
+ onFailure: () => never,
335
+ onSuccess: () => never,
336
+ }), unwrap));
337
+ const hasTag = (u) => typeof u === "object" &&
338
+ u !== null &&
339
+ "_tag" in u &&
340
+ typeof u["_tag"] === "string";
341
+ const matchesTag = (tag, error) => {
342
+ if (!hasTag(error))
343
+ return false;
344
+ if (typeof tag === "string")
345
+ return error._tag === tag;
346
+ return tag.some((t) => t === error._tag);
347
+ };
348
+ function normalizeDependencies(dependencies) {
349
+ return dependencies.map((dep) => Layer.isLayer(dep)
350
+ ? dep
351
+ : Layer.succeedServices(dep));
352
+ }
353
+ function getGuard(guard) {
354
+ return "asGuard" in guard ? guard.asGuard() : guard;
355
+ }
356
+ function defaultGuard() {
357
+ return Effect.succeedSome;
358
+ }
359
+ function mergeLayers(layers) {
360
+ if (layers.length === 1)
361
+ return layers[0];
362
+ let current = layers[0];
363
+ for (let i = 1; i < layers.length; i++) {
364
+ current = Layer.merge(current, layers[i]);
365
+ }
366
+ return current;
367
+ }
368
+ /**
369
+ * @internal
370
+ */
371
+ export function compile(cases) {
372
+ const entries = [];
373
+ const visit = (matches, context) => {
374
+ for (const match of matches) {
375
+ switch (match.type) {
376
+ case "route": {
377
+ const baseRoute = makeRoute(match.route);
378
+ const prefixedRoute = applyPrefixes(baseRoute, context.prefixes);
379
+ entries.push({
380
+ route: prefixedRoute,
381
+ guard: getGuard(match.guard),
382
+ handler: normalizeHandler(match.handler),
383
+ layers: context.layers,
384
+ layouts: context.layouts,
385
+ catches: context.catches,
386
+ decode: Schema.decodeUnknownEffect(prefixedRoute.paramsSchema),
387
+ });
388
+ break;
389
+ }
390
+ case "layer": {
391
+ const merged = mergeLayers(match.deps);
392
+ visit(match.matches, {
393
+ ...context,
394
+ layers: [...context.layers, merged],
395
+ });
396
+ break;
397
+ }
398
+ case "layout": {
399
+ visit(match.matches, {
400
+ ...context,
401
+ layouts: [...context.layouts, match.layout],
402
+ });
403
+ break;
404
+ }
405
+ case "prefixed": {
406
+ visit(match.matches, {
407
+ ...context,
408
+ prefixes: [...context.prefixes, match.prefix],
409
+ });
410
+ break;
411
+ }
412
+ case "catch": {
413
+ visit(match.matches, {
414
+ ...context,
415
+ catches: [...context.catches, match.f],
416
+ });
417
+ break;
418
+ }
419
+ }
420
+ }
421
+ };
422
+ visit(cases, { layers: [], layouts: [], catches: [], prefixes: [] });
423
+ return entries;
424
+ }
425
+ function applyPrefixes(route, prefixes) {
426
+ if (prefixes.length === 0)
427
+ return route;
428
+ const prefixRoutes = prefixes.map((prefix) => makeRoute(prefix));
429
+ return Join(...prefixRoutes, route);
430
+ }
431
+ // Parallel scope cleanup helper
432
+ const closeScopes = (scopes, fiberId) => Effect.forEach(scopes, (scope) => Scope.close(scope, interrupt(fiberId)), {
433
+ concurrency: "unbounded",
434
+ discard: true,
435
+ });
436
+ /**
437
+ * @internal
438
+ */
439
+ export function makeLayerManager(memoMap, rootScope, fiberId) {
440
+ const states = new Map();
441
+ let order = [];
442
+ let cachedDesiredSet = undefined;
443
+ let cachedOrder = undefined;
444
+ const prepare = (desired) => Effect.gen(function* () {
445
+ const desiredSet = cachedOrder === desired
446
+ ? cachedDesiredSet
447
+ : ((cachedDesiredSet = new Set(desired)), (cachedOrder = desired), cachedDesiredSet);
448
+ const removed = order.filter((layer) => !desiredSet.has(layer));
449
+ const added = [];
450
+ let services = ServiceMap.empty();
451
+ for (const layer of desired) {
452
+ const existing = states.get(layer);
453
+ if (existing) {
454
+ services = ServiceMap.merge(services, existing.services);
455
+ continue;
456
+ }
457
+ const scope = yield* Scope.fork(rootScope);
458
+ const buildExit = yield* Layer.buildWithMemoMap(layer, memoMap, scope).pipe(Effect.provideServices(services), Effect.exit);
459
+ if (Exit.isFailure(buildExit)) {
460
+ for (let i = added.length - 1; i >= 0; i--) {
461
+ const addedLayer = added[i];
462
+ const addedState = states.get(addedLayer);
463
+ if (addedState) {
464
+ states.delete(addedLayer);
465
+ yield* Scope.close(addedState.scope, interrupt(fiberId));
466
+ }
467
+ }
468
+ yield* Scope.close(scope, buildExit);
469
+ return yield* Effect.failCause(buildExit.cause);
470
+ }
471
+ const servicesForLayer = buildExit.value;
472
+ services = ServiceMap.merge(services, servicesForLayer);
473
+ states.set(layer, { scope, services: servicesForLayer });
474
+ added.push(layer);
475
+ }
476
+ const commit = Effect.gen(function* () {
477
+ for (let i = removed.length - 1; i >= 0; i--) {
478
+ const layer = removed[i];
479
+ const state = states.get(layer);
480
+ if (state) {
481
+ states.delete(layer);
482
+ yield* Scope.close(state.scope, interrupt(fiberId));
483
+ }
484
+ }
485
+ order = desired;
486
+ });
487
+ const rollback = Effect.gen(function* () {
488
+ for (let i = added.length - 1; i >= 0; i--) {
489
+ const layer = added[i];
490
+ const state = states.get(layer);
491
+ if (state) {
492
+ states.delete(layer);
493
+ yield* Scope.close(state.scope, interrupt(fiberId));
494
+ }
495
+ }
496
+ });
497
+ return { services, commit, rollback };
498
+ });
499
+ return { prepare };
500
+ }
501
+ /**
502
+ * @internal
503
+ */
504
+ export function makeLayoutManager(rootScope, fiberId) {
505
+ const states = new Map();
506
+ let active = [];
507
+ const removeUnused = (layouts) => Effect.gen(function* () {
508
+ const next = new Set(layouts);
509
+ const removed = active.filter((layout) => !next.has(layout));
510
+ const scopes = removed.map((layout) => {
511
+ const state = states.get(layout);
512
+ states.delete(layout);
513
+ return state.scope;
514
+ });
515
+ yield* closeScopes(scopes, fiberId);
516
+ active = layouts;
517
+ });
518
+ const apply = (layouts, paramsValue, inner, services) => Effect.gen(function* () {
519
+ let current = inner;
520
+ for (let i = layouts.length - 1; i >= 0; i--) {
521
+ const layout = layouts[i];
522
+ const state = states.get(layout);
523
+ if (state === undefined) {
524
+ const scope = yield* Scope.fork(rootScope);
525
+ const params = yield* RefSubject.make(paramsValue).pipe(Scope.provide(scope));
526
+ const content = yield* RefSubject.make(Effect.succeed(current), {
527
+ eq: (left, right) => left === right,
528
+ }).pipe(Scope.provide(scope));
529
+ const fx = layout({ params, content: content.pipe(switchMap(identity)) }).pipe(provideServices(ServiceMap.merge(services, ServiceMap.make(Scope.Scope, scope))));
530
+ states.set(layout, { params, content, fx, scope });
531
+ current = fx;
532
+ }
533
+ else {
534
+ yield* RefSubject.set(state.params, paramsValue);
535
+ // @effect-diagnostics-next-line floatingEffect:off
536
+ yield* RefSubject.set(state.content, current);
537
+ current = state.fx;
538
+ }
539
+ }
540
+ yield* removeUnused(layouts);
541
+ return current;
542
+ });
543
+ const updateParams = (layouts, paramsValue) => Effect.forEach(layouts, (layout) => {
544
+ const state = states.get(layout);
545
+ return state !== undefined ? RefSubject.set(state.params, paramsValue) : Effect.void;
546
+ }, { discard: true });
547
+ return { apply, updateParams };
548
+ }
549
+ /**
550
+ * @internal
551
+ */
552
+ export function makeCatchManager(rootScope, fiberId) {
553
+ const states = new Map();
554
+ let active = [];
555
+ const removeUnused = (catches) => Effect.gen(function* () {
556
+ const next = new Set(catches);
557
+ const removed = active.filter((c) => !next.has(c));
558
+ const scopes = removed.map((c) => {
559
+ const state = states.get(c);
560
+ states.delete(c);
561
+ return state.scope;
562
+ });
563
+ yield* closeScopes(scopes, fiberId);
564
+ active = catches;
565
+ });
566
+ const apply = (catches, inner, services) => Effect.gen(function* () {
567
+ let current = inner;
568
+ for (let i = catches.length - 1; i >= 0; i--) {
569
+ const catcher = catches[i];
570
+ const state = states.get(catcher);
571
+ if (state === undefined) {
572
+ const scope = yield* Scope.fork(rootScope);
573
+ const causes = yield* RefSubject.make(Cause.fail(undefined)).pipe(Scope.provide(scope));
574
+ const content = yield* RefSubject.make(Effect.succeed(current), {
575
+ eq: (left, right) => left === right,
576
+ }).pipe(Scope.provide(scope));
577
+ const fallback = catcher(causes).pipe(provideServices(ServiceMap.merge(services, ServiceMap.make(Scope.Scope, scope))));
578
+ const fx = content.pipe(switchMap(identity), exit, mapEffect(Effect.fn(function* (e) {
579
+ if (isSuccess(e))
580
+ return succeed(e.value);
581
+ yield* RefSubject.set(causes, e.cause);
582
+ return fallback;
583
+ })), skipRepeats, switchMap(identity));
584
+ states.set(catcher, { causes, content, fx, scope });
585
+ current = fx;
586
+ }
587
+ else {
588
+ // @effect-diagnostics-next-line floatingEffect:off
589
+ yield* RefSubject.set(state.content, current);
590
+ current = state.fx;
591
+ }
592
+ }
593
+ yield* removeUnused(catches);
594
+ return current;
595
+ });
596
+ return { apply };
597
+ }
@@ -0,0 +1,96 @@
1
+ import type { Arg0, Pipe, TypeLambda, TypeLambda1 } from "hkt-core";
2
+ export type Result<Value, Rest extends string> = readonly [value: Value, rest: Rest];
3
+ export interface Parser<Output = unknown> extends TypeLambda<[
4
+ input: string
5
+ ], Result<Output, string>> {
6
+ }
7
+ export type Parse<P extends Parser<unknown>, Input extends string> = Pipe<Input, P>;
8
+ type IsStringLiteral<T extends string> = string extends T ? false : true;
9
+ type StrictEquals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
10
+ type IsNoProgress<Input extends string, Rest extends string> = IsStringLiteral<Input> extends true ? StrictEquals<Input, Rest> : false;
11
+ export declare namespace Parser {
12
+ type Any = Parser<unknown>;
13
+ type Run<P extends Any, Input extends string> = Pipe<Input, P>;
14
+ interface Succeed<A> extends TypeLambda<[input: string], Result<A, string>> {
15
+ readonly return: Arg0<this> extends infer Input extends string ? readonly [A, Input] : never;
16
+ }
17
+ interface Fail extends TypeLambda<[input: string], never> {
18
+ readonly return: never;
19
+ }
20
+ interface Char<C extends string> extends TypeLambda<[
21
+ input: string
22
+ ], Result<C, string> | never> {
23
+ readonly return: Arg0<this> extends `${C}${infer Rest}` ? readonly [C, Rest] : never;
24
+ }
25
+ interface String<S extends string> extends TypeLambda<[
26
+ input: string
27
+ ], Result<S, string> | never> {
28
+ readonly return: Arg0<this> extends `${S}${infer Rest}` ? readonly [S, Rest] : never;
29
+ }
30
+ type LowercaseAlphabet = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
31
+ type UppercaseAlphabet = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
32
+ type Alphabet = LowercaseAlphabet | UppercaseAlphabet;
33
+ type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
34
+ type AlphaNumeric = Alphabet | Digit;
35
+ type TakeWhileInternal<Input extends string, Allowed extends string, Acc extends string = ""> = Input extends `${infer Head}${infer Tail}` ? Head extends Allowed ? TakeWhileInternal<Tail, Allowed, `${Acc}${Head}`> : readonly [Acc, Input] : readonly [Acc, Input];
36
+ type TakeWhile1Internal<Input extends string, Allowed extends string> = TakeWhileInternal<Input, Allowed> extends readonly [
37
+ infer Taken extends string,
38
+ infer Rest extends string
39
+ ] ? Taken extends "" ? never : readonly [Taken, Rest] : never;
40
+ interface TakeWhile<Allowed extends string> extends TypeLambda<[
41
+ input: string
42
+ ], Result<string, string>> {
43
+ readonly return: Arg0<this> extends infer Input extends string ? TakeWhileInternal<Input, Allowed> : never;
44
+ }
45
+ interface TakeWhile1<Allowed extends string> extends TypeLambda<[
46
+ input: string
47
+ ], Result<string, string> | never> {
48
+ readonly return: Arg0<this> extends infer Input extends string ? TakeWhile1Internal<Input, Allowed> : never;
49
+ }
50
+ interface Map<P extends Any, F extends TypeLambda1> extends Parser<unknown> {
51
+ readonly return: Arg0<this> extends infer Input extends string ? Pipe<Input, P> extends infer R ? [R] extends [never] ? never : R extends readonly [infer Value, infer Rest extends string] ? readonly [Pipe<Value, F>, Rest] : never : never : never;
52
+ }
53
+ interface FlatMap<P extends Any, F extends TypeLambda1> extends Parser<unknown> {
54
+ readonly return: Arg0<this> extends infer Input extends string ? Pipe<Input, P> extends infer R ? [R] extends [never] ? never : R extends readonly [infer Value, infer Rest extends string] ? Pipe<Value, F> extends infer Next ? [Next] extends [never] ? never : Next extends Any ? Pipe<Rest, Next> : never : never : never : never : never;
55
+ }
56
+ interface Zip<P extends Any, Q extends Any> extends Parser<unknown> {
57
+ readonly return: Arg0<this> extends infer Input extends string ? Pipe<Input, P> extends infer R1 ? [R1] extends [never] ? never : R1 extends readonly [infer Value1, infer Rest1 extends string] ? Pipe<Rest1, Q> extends infer R2 ? [R2] extends [never] ? never : R2 extends readonly [infer Value2, infer Rest2 extends string] ? readonly [readonly [Value1, Value2], Rest2] : never : never : never : never : never;
58
+ }
59
+ interface OrElse<P extends Any, Q extends Any> extends Parser<unknown> {
60
+ readonly return: Arg0<this> extends infer Input extends string ? Pipe<Input, P> extends infer R ? [R] extends [never] ? Pipe<Input, Q> : R extends readonly [infer Value, infer Rest extends string] ? readonly [Value, Rest] : never : never : never;
61
+ }
62
+ interface Optional<P extends Any> extends Parser<unknown> {
63
+ readonly return: Arg0<this> extends infer Input extends string ? Pipe<Input, P> extends infer R ? [R] extends [never] ? readonly [undefined, Input] : R extends readonly [infer Value, infer Rest extends string] ? readonly [Value, Rest] : never : never : never;
64
+ }
65
+ type ManyInternal<P extends Any, Input extends string, Acc extends ReadonlyArray<unknown> = readonly []> = Pipe<Input, P> extends infer R ? [R] extends [never] ? readonly [Acc, Input] : R extends readonly [infer Value, infer Rest extends string] ? IsNoProgress<Input, Rest> extends true ? never : ManyInternal<P, Rest, readonly [...Acc, Value]> : never : never;
66
+ type Many1Internal<P extends Any, Input extends string> = Pipe<Input, P> extends infer R ? [R] extends [never] ? never : R extends readonly [infer Value, infer Rest extends string] ? IsNoProgress<Input, Rest> extends true ? never : ManyInternal<P, Rest, readonly [Value]> : never : never;
67
+ interface Many<P extends Any> extends Parser<unknown> {
68
+ readonly return: Arg0<this> extends infer Input extends string ? ManyInternal<P, Input> : never;
69
+ }
70
+ interface Many1<P extends Any> extends Parser<unknown> {
71
+ readonly return: Arg0<this> extends infer Input extends string ? Many1Internal<P, Input> : never;
72
+ }
73
+ interface MapTo<F extends TypeLambda1> extends TypeLambda1 {
74
+ readonly return: Arg0<this> extends infer P extends Any ? Map<P, F> : never;
75
+ }
76
+ interface FlatMapTo<F extends TypeLambda1> extends TypeLambda1 {
77
+ readonly return: Arg0<this> extends infer P extends Any ? FlatMap<P, F> : never;
78
+ }
79
+ interface ZipWith<Q extends Any> extends TypeLambda1 {
80
+ readonly return: Arg0<this> extends infer P extends Any ? Zip<P, Q> : never;
81
+ }
82
+ interface OrElseWith<Q extends Any> extends TypeLambda1 {
83
+ readonly return: Arg0<this> extends infer P extends Any ? OrElse<P, Q> : never;
84
+ }
85
+ interface OptionalOf extends TypeLambda1 {
86
+ readonly return: Arg0<this> extends infer P extends Any ? Optional<P> : never;
87
+ }
88
+ interface ManyOf extends TypeLambda1 {
89
+ readonly return: Arg0<this> extends infer P extends Any ? Many<P> : never;
90
+ }
91
+ interface Many1Of extends TypeLambda1 {
92
+ readonly return: Arg0<this> extends infer P extends Any ? Many1<P> : never;
93
+ }
94
+ }
95
+ export {};
96
+ //# sourceMappingURL=Parser.d.ts.map