@typed/navigation 0.18.0 → 0.18.1

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 (113) hide show
  1. package/.nvmrc +1 -0
  2. package/biome.json +39 -0
  3. package/dist/Blocking.d.ts +23 -0
  4. package/dist/Blocking.js +41 -0
  5. package/dist/Blocking.js.map +1 -0
  6. package/dist/Destination.d.ts +11 -0
  7. package/dist/Destination.js +10 -0
  8. package/dist/Destination.js.map +1 -0
  9. package/dist/Error.d.ts +33 -0
  10. package/dist/Error.js +22 -0
  11. package/dist/Error.js.map +1 -0
  12. package/dist/Event.d.ts +45 -0
  13. package/dist/Event.js +17 -0
  14. package/dist/Event.js.map +1 -0
  15. package/dist/Forms.d.ts +79 -0
  16. package/dist/Forms.js +111 -0
  17. package/dist/Forms.js.map +1 -0
  18. package/dist/Handler.d.ts +6 -0
  19. package/dist/Handler.js +2 -0
  20. package/dist/Handler.js.map +1 -0
  21. package/dist/{dts/Layer.d.ts → Layer.d.ts} +11 -8
  22. package/dist/{esm/Layer.js → Layer.js} +2 -2
  23. package/dist/Layer.js.map +1 -0
  24. package/dist/NavigateOptions.d.ts +7 -0
  25. package/dist/NavigateOptions.js +7 -0
  26. package/dist/NavigateOptions.js.map +1 -0
  27. package/dist/Navigation.d.ts +79 -0
  28. package/dist/Navigation.js +49 -0
  29. package/dist/Navigation.js.map +1 -0
  30. package/dist/NavigationType.d.ts +3 -0
  31. package/dist/NavigationType.js +3 -0
  32. package/dist/NavigationType.js.map +1 -0
  33. package/dist/ProposedDestination.d.ts +13 -0
  34. package/dist/ProposedDestination.js +4 -0
  35. package/dist/ProposedDestination.js.map +1 -0
  36. package/dist/Url.d.ts +13 -0
  37. package/dist/Url.js +72 -0
  38. package/dist/Url.js.map +1 -0
  39. package/dist/index.d.ts +12 -0
  40. package/dist/index.js +13 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/internal/fromWindow.d.ts +4 -0
  43. package/dist/{esm/internal → internal}/fromWindow.js +125 -136
  44. package/dist/internal/fromWindow.js.map +1 -0
  45. package/dist/internal/memory.d.ts +6 -0
  46. package/dist/internal/memory.js +59 -0
  47. package/dist/internal/memory.js.map +1 -0
  48. package/dist/{dts/internal → internal}/shared.d.ts +44 -46
  49. package/dist/{esm/internal → internal}/shared.js +121 -168
  50. package/dist/internal/shared.js.map +1 -0
  51. package/package.json +35 -53
  52. package/readme.md +243 -0
  53. package/src/Blocking.ts +65 -65
  54. package/src/Destination.ts +14 -0
  55. package/src/Error.ts +28 -0
  56. package/src/Event.ts +26 -0
  57. package/src/Forms.ts +216 -0
  58. package/src/Handler.ts +16 -0
  59. package/src/Layer.ts +20 -9
  60. package/src/NavigateOptions.ts +9 -0
  61. package/src/Navigation.test.ts +697 -0
  62. package/src/Navigation.ts +135 -472
  63. package/src/NavigationType.ts +5 -0
  64. package/src/ProposedDestination.ts +8 -0
  65. package/src/Url.ts +106 -0
  66. package/src/index.ts +12 -17
  67. package/src/internal/fromWindow.ts +163 -234
  68. package/src/internal/memory.ts +62 -49
  69. package/src/internal/shared.ts +218 -377
  70. package/tsconfig.json +30 -0
  71. package/Blocking/package.json +0 -6
  72. package/LICENSE +0 -21
  73. package/Layer/package.json +0 -6
  74. package/Navigation/package.json +0 -6
  75. package/README.md +0 -5
  76. package/dist/cjs/Blocking.js +0 -58
  77. package/dist/cjs/Blocking.js.map +0 -1
  78. package/dist/cjs/Layer.js +0 -27
  79. package/dist/cjs/Layer.js.map +0 -1
  80. package/dist/cjs/Navigation.js +0 -278
  81. package/dist/cjs/Navigation.js.map +0 -1
  82. package/dist/cjs/index.js +0 -39
  83. package/dist/cjs/index.js.map +0 -1
  84. package/dist/cjs/internal/fromWindow.js +0 -436
  85. package/dist/cjs/internal/fromWindow.js.map +0 -1
  86. package/dist/cjs/internal/memory.js +0 -72
  87. package/dist/cjs/internal/memory.js.map +0 -1
  88. package/dist/cjs/internal/shared.js +0 -525
  89. package/dist/cjs/internal/shared.js.map +0 -1
  90. package/dist/dts/Blocking.d.ts +0 -34
  91. package/dist/dts/Blocking.d.ts.map +0 -1
  92. package/dist/dts/Layer.d.ts.map +0 -1
  93. package/dist/dts/Navigation.d.ts +0 -451
  94. package/dist/dts/Navigation.d.ts.map +0 -1
  95. package/dist/dts/index.d.ts +0 -17
  96. package/dist/dts/index.d.ts.map +0 -1
  97. package/dist/dts/internal/fromWindow.d.ts +0 -12
  98. package/dist/dts/internal/fromWindow.d.ts.map +0 -1
  99. package/dist/dts/internal/memory.d.ts +0 -6
  100. package/dist/dts/internal/memory.d.ts.map +0 -1
  101. package/dist/dts/internal/shared.d.ts.map +0 -1
  102. package/dist/esm/Blocking.js +0 -46
  103. package/dist/esm/Blocking.js.map +0 -1
  104. package/dist/esm/Layer.js.map +0 -1
  105. package/dist/esm/Navigation.js +0 -238
  106. package/dist/esm/Navigation.js.map +0 -1
  107. package/dist/esm/index.js +0 -17
  108. package/dist/esm/index.js.map +0 -1
  109. package/dist/esm/internal/fromWindow.js.map +0 -1
  110. package/dist/esm/internal/memory.js +0 -56
  111. package/dist/esm/internal/memory.js.map +0 -1
  112. package/dist/esm/internal/shared.js.map +0 -1
  113. package/dist/esm/package.json +0 -4
package/src/Forms.ts ADDED
@@ -0,0 +1,216 @@
1
+ import type { HttpBody } from '@effect/platform'
2
+ import type { Options } from '@effect/platform/HttpClientRequest'
3
+ import * as Array from 'effect/Array'
4
+ import * as Effect from 'effect/Effect'
5
+ import * as ParseResult from 'effect/ParseResult'
6
+ import * as Schema from 'effect/Schema'
7
+ import * as SchemaAST from 'effect/SchemaAST'
8
+ import type * as Types from 'effect/Types'
9
+ import type { NavigateOptions } from './NavigateOptions.js'
10
+ import { UrlSearchParamsFromSelf } from './Url.js'
11
+
12
+ export const BlobFromSelf = Schema.instanceOf(globalThis.Blob)
13
+ export type BlobFromSelf = typeof BlobFromSelf.Type
14
+
15
+ export const BlobFrom = Schema.TaggedStruct('Blob', {
16
+ type: Schema.String,
17
+ size: Schema.Number,
18
+ data: Schema.Uint8Array,
19
+ })
20
+
21
+ export type BlobEncoded = typeof BlobFrom.Encoded
22
+ export type BlobFrom = typeof BlobFrom.Type
23
+
24
+ export const Blob: Schema.Schema<Blob, BlobEncoded> = BlobFrom.pipe(
25
+ Schema.transformOrFail(BlobFromSelf, {
26
+ strict: true,
27
+ decode: (blob) => Effect.succeed(new globalThis.Blob([blob.data], { type: blob.type })),
28
+ encode: (blob) =>
29
+ Effect.promise(() =>
30
+ blob
31
+ .arrayBuffer()
32
+ .then((data) =>
33
+ BlobFrom.make({ type: blob.type, size: blob.size, data: new Uint8Array(data) }),
34
+ ),
35
+ ),
36
+ }),
37
+ )
38
+
39
+ export const FileFromSelf = Schema.instanceOf(globalThis.File)
40
+ export type FileFromSelf = typeof FileFromSelf.Type
41
+
42
+ export const FileFrom = Schema.TaggedStruct('File', {
43
+ name: Schema.String,
44
+ size: Schema.Number,
45
+ type: Schema.String,
46
+ lastModified: Schema.DateFromNumber,
47
+ data: Schema.Uint8Array,
48
+ })
49
+
50
+ export type FileEncoded = typeof FileFrom.Encoded
51
+ export type FileFrom = typeof FileFrom.Type
52
+
53
+ export const File: Schema.Schema<File, FileEncoded> = FileFrom.pipe(
54
+ Schema.transformOrFail(FileFromSelf, {
55
+ strict: true,
56
+ decode: (file) =>
57
+ Effect.succeed(
58
+ new globalThis.File([file.data], file.name, {
59
+ type: file.type,
60
+ lastModified: file.lastModified.getTime(),
61
+ }),
62
+ ),
63
+ encode: (file) =>
64
+ Effect.promise(() =>
65
+ file.arrayBuffer().then((data) =>
66
+ FileFrom.make({
67
+ name: file.name,
68
+ size: file.size,
69
+ type: file.type,
70
+ lastModified: new Date(file.lastModified),
71
+ data: new Uint8Array(data),
72
+ }),
73
+ ),
74
+ ),
75
+ }),
76
+ )
77
+
78
+ export const FormDataFromSelf = Schema.instanceOf(globalThis.FormData)
79
+ export type FormDataFromSelf = typeof FormDataFromSelf.Type
80
+
81
+ export const FormDataEntryValue = Schema.Union(Blob, File, Schema.String)
82
+ export type FormDataEntryValue = typeof FormDataEntryValue.Type
83
+ export type FormDataEntryValueEncoded = typeof FormDataEntryValue.Encoded
84
+
85
+ export const FormDataFrom = Schema.Record({
86
+ key: Schema.String,
87
+ value: FormDataEntryValue,
88
+ })
89
+
90
+ export type FormDataEncoded = typeof FormDataFrom.Encoded
91
+ export type FormDataFrom = typeof FormDataFrom.Type
92
+
93
+ export const FormData: Schema.Schema<FormData, FormDataEncoded> = FormDataFrom.pipe(
94
+ Schema.transform(FormDataFromSelf, {
95
+ strict: true,
96
+ decode: structToFormData,
97
+ encode: formDataToStruct,
98
+ }),
99
+ )
100
+
101
+ type AnySchemaEncoded<T> = Schema.Schema<any, T, any> | Schema.Schema<any, T, never>
102
+
103
+ type AnyFormDataFieldSchema =
104
+ | AnySchemaEncoded<string>
105
+ | AnySchemaEncoded<Blob>
106
+ | AnySchemaEncoded<File>
107
+ | AnySchemaEncoded<string | null>
108
+ | AnySchemaEncoded<Blob | null>
109
+ | AnySchemaEncoded<File | null>
110
+
111
+ type FormDataFields = Record<string, AnyFormDataFieldSchema>
112
+
113
+ export function schemaFormData<const Fields extends FormDataFields>(
114
+ fields: Fields,
115
+ ): Schema.Schema<
116
+ { readonly [K in keyof Schema.Struct.Type<Fields>]: Schema.Struct.Type<Fields>[K] },
117
+ FormData,
118
+ Schema.Schema.Context<Fields[keyof Fields]>
119
+ > {
120
+ return Schema.transform(FormDataFromSelf, Schema.Struct(fields), {
121
+ strict: false,
122
+ decode: formDataToStruct,
123
+ encode: structToFormData as any,
124
+ })
125
+ }
126
+
127
+ function formDataToStruct(formData: FormData): Record<string, FormDataEntryValue> {
128
+ const record: Record<string, FormDataEntryValue> = {}
129
+ formData.forEach((value, key) => {
130
+ record[key] = value
131
+ })
132
+ return record
133
+ }
134
+
135
+ function structToFormData(struct: Record<string, FormDataEntryValue>): FormData {
136
+ const formData = new globalThis.FormData()
137
+ for (const [key, value] of Object.entries(struct)) {
138
+ formData.append(key, value)
139
+ }
140
+ return formData
141
+ }
142
+
143
+ export type FormGetSubmit = Types.Simplify<
144
+ Options.NoBody &
145
+ NavigateOptions & {
146
+ readonly method: 'get'
147
+ readonly name: string
148
+ readonly action?: string | URL
149
+ }
150
+ >
151
+
152
+ export type FormPostSubmit = Types.Simplify<
153
+ Omit<FormGetSubmit, 'method'> & {
154
+ readonly method: 'post'
155
+ readonly body?: HttpBody.HttpBody
156
+ }
157
+ >
158
+
159
+ export type FormSubmit = FormGetSubmit | FormPostSubmit
160
+
161
+ export const FormDataFromUrlSearchParams: Schema.Schema<FormData, URLSearchParams> =
162
+ UrlSearchParamsFromSelf.pipe(
163
+ Schema.transformOrFail(FormDataFromSelf, {
164
+ strict: true,
165
+ decode: (searchParams) => Effect.succeed(searchParamsToFormData(searchParams)),
166
+ encode: (formData) =>
167
+ Effect.gen(function* () {
168
+ const searchParams = new globalThis.URLSearchParams()
169
+ const errors: ParseResult.ParseIssue[] = []
170
+
171
+ for (const [key, value] of Object.entries(formDataToStruct(formData))) {
172
+ if (typeof value === 'string') {
173
+ searchParams.set(key, value)
174
+ } else {
175
+ errors.push(
176
+ new ParseResult.Type(
177
+ SchemaAST.stringKeyword,
178
+ value,
179
+ `Expected string at '${key}' but found ${value instanceof globalThis.File ? 'File' : 'Blob'}`,
180
+ ),
181
+ )
182
+ }
183
+ }
184
+
185
+ if (Array.isNonEmptyReadonlyArray(errors)) {
186
+ return yield* ParseResult.fail(
187
+ new ParseResult.Composite(SchemaAST.stringKeyword, formData, errors),
188
+ )
189
+ }
190
+
191
+ return searchParams
192
+ }),
193
+ }),
194
+ )
195
+
196
+ export const UrlSearchParamsToFormData: Schema.Schema<URLSearchParams, FormData> = swap(
197
+ FormDataFromUrlSearchParams,
198
+ )
199
+
200
+ function searchParamsToFormData(searchParams: URLSearchParams): FormData {
201
+ const formData = new globalThis.FormData()
202
+ searchParams.forEach((value, key) => {
203
+ formData.append(key, value)
204
+ })
205
+ return formData
206
+ }
207
+
208
+ function swap<A, I, R>(schema: Schema.Schema<A, I, R>): Schema.Schema<I, A, R> {
209
+ const encode = ParseResult.encode(schema)
210
+ const decode = ParseResult.decode(schema)
211
+ return Schema.transformOrFail(Schema.typeSchema(schema), Schema.encodedSchema(schema), {
212
+ strict: true,
213
+ decode: encode,
214
+ encode: decode,
215
+ })
216
+ }
package/src/Handler.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type * as Effect from 'effect/Effect'
2
+ import type * as Option from 'effect/Option'
3
+ import type { CancelNavigation, RedirectError } from './Error.js'
4
+ import type { NavigationEvent, TransitionEvent } from './Event.js'
5
+
6
+ export type BeforeNavigationHandler<R, R2> = (event: TransitionEvent) => Effect.Effect<
7
+ // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
8
+ void | Option.Option<Effect.Effect<unknown, RedirectError | CancelNavigation, R2>>,
9
+ RedirectError | CancelNavigation,
10
+ R
11
+ >
12
+
13
+ export type NavigationHandler<R, R2> = (
14
+ event: NavigationEvent,
15
+ // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
16
+ ) => Effect.Effect<void | Option.Option<Effect.Effect<unknown, never, R2>>, never, R>
package/src/Layer.ts CHANGED
@@ -2,16 +2,21 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
 
5
- import type * as Effect from "effect/Effect"
6
- import type * as Layer from "effect/Layer"
7
- import * as internalFromWindow from "./internal/fromWindow.js"
8
- import * as internalMemory from "./internal/memory.js"
9
- import type { BeforeNavigationEvent, Destination, Navigation, NavigationError } from "./Navigation.js"
5
+ import type { GetRandomValues } from '@typed/id'
6
+ import type * as Effect from 'effect/Effect'
7
+ import type * as Layer from 'effect/Layer'
8
+ import type { Destination } from './Destination.js'
9
+ import type { NavigationError } from './Error.js'
10
+ import type { TransitionEvent } from './Event.js'
11
+ import * as internalFromWindow from './internal/fromWindow.js'
12
+ import * as internalMemory from './internal/memory.js'
13
+ import type { Navigation } from './Navigation.js'
10
14
 
11
15
  /**
12
16
  * @since 1.0.0
13
17
  */
14
- export const fromWindow: Layer.Layer<Navigation, never, Window> = internalFromWindow.fromWindow
18
+ export const fromWindow: (window: Window) => Layer.Layer<Navigation, never, GetRandomValues> =
19
+ internalFromWindow.fromWindow
15
20
 
16
21
  /**
17
22
  * @since 1.0.0
@@ -28,7 +33,8 @@ export interface MemoryOptions {
28
33
  /**
29
34
  * @since 1.0.0
30
35
  */
31
- export const memory: (options: MemoryOptions) => Layer.Layer<Navigation> = internalMemory.memory
36
+ export const memory: (options: MemoryOptions) => Layer.Layer<Navigation, never, GetRandomValues> =
37
+ internalMemory.memory
32
38
 
33
39
  /**
34
40
  * @since 1.0.0
@@ -45,9 +51,14 @@ export interface InitialMemoryOptions {
45
51
  /**
46
52
  * @since 1.0.0
47
53
  */
48
- export const initialMemory: (options: InitialMemoryOptions) => Layer.Layer<Navigation> = internalMemory.initialMemory
54
+ export const initialMemory: (
55
+ options: InitialMemoryOptions,
56
+ ) => Layer.Layer<Navigation, never, GetRandomValues> = internalMemory.initialMemory
49
57
 
50
58
  /**
51
59
  * @since 1.0.0
52
60
  */
53
- export type Commit = (to: Destination, event: BeforeNavigationEvent) => Effect.Effect<void, NavigationError>
61
+ export type Commit = (
62
+ to: Destination,
63
+ event: TransitionEvent,
64
+ ) => Effect.Effect<void, NavigationError>
@@ -0,0 +1,9 @@
1
+ import * as Schema from 'effect/Schema'
2
+
3
+ export const NavigateOptions = Schema.Struct({
4
+ history: Schema.optional(Schema.Union(Schema.Literal('replace', 'push', 'auto'))),
5
+ state: Schema.optional(Schema.Unknown),
6
+ info: Schema.optional(Schema.Unknown),
7
+ })
8
+
9
+ export type NavigateOptions = Schema.Schema.Type<typeof NavigateOptions>