@jon49/sw 0.15.2 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,240 +2,246 @@ import { SwResponse } from "./sw-framework.js"
2
2
  import { isHtml } from "./utils.js"
3
3
 
4
4
  let { links, globalDb } =
5
- // @ts-ignore
6
- self.sw as { links: { file: string, url: string }[], html: Function, db: any, globalDb: any }
5
+ // @ts-ignore
6
+ self.sw as { links: { file: string, url: string }[], html: Function, db: any, globalDb: any }
7
7
 
8
8
  if (!links) {
9
- console.error("Expecting links defined with `self.sw.links`, but found none.")
9
+ console.error("Expecting links defined with `self.sw.links`, but found none.")
10
10
  }
11
11
 
12
12
  function redirect(req: Request) {
13
- return Response.redirect(req.referrer, 303)
13
+ return Response.redirect(req.referrer, 303)
14
14
  }
15
15
 
16
16
  const searchParamsHandler = {
17
- get(obj: any, prop: string) {
18
- if (prop === "_url") {
19
- return obj
20
- }
21
- return obj.searchParams.get(prop)
17
+ get(obj: any, prop: string) {
18
+ if (prop === "_url") {
19
+ return obj
22
20
  }
21
+ return obj.searchParams.get(prop)
22
+ }
23
23
  }
24
24
 
25
25
  function searchParams<TReturn>(url: URL): TReturn & { _url: URL } {
26
- return new Proxy(url, searchParamsHandler)
26
+ return new Proxy(url, searchParamsHandler)
27
27
  }
28
28
 
29
29
  interface ResponseOptions {
30
- handleErrors?: Function
30
+ handleErrors?: Function
31
31
  }
32
32
 
33
33
  export let options: ResponseOptions = {}
34
34
 
35
35
  // @ts-ignore
36
36
  export async function useRoutes(req: Request, res: SwResponse, ctx: any): Promise<void> {
37
- try {
38
- const url = normalizeUrl(req.url)
39
- if (!url.pathname.startsWith("/web/")) {
40
- res.response = await fetch(req)
41
- } else {
42
- Object.assign(res, await executeHandler({ url, req, ctx }))
43
- }
44
- } catch (error) {
45
- console.error("Get Response Error", error)
46
- res.error = "Oops something happened which shouldn't have!"
37
+ try {
38
+ const url = normalizeUrl(req.url)
39
+ if (!url.pathname.startsWith("/web/")) {
40
+ res.response = await fetch(req)
41
+ } else {
42
+ Object.assign(res, await executeHandler({ url, req, ctx }))
47
43
  }
44
+ } catch (error) {
45
+ console.error("Get Response Error", error)
46
+ res.error = "Oops something happened which shouldn't have!"
47
+ }
48
48
  }
49
49
 
50
50
  function call(fn: Function | undefined, args: any) {
51
- return fn instanceof Function && fn(args)
51
+ return fn instanceof Function && fn(args)
52
52
  }
53
53
 
54
54
  const methodTypes = ['get', 'post'] as const
55
55
  type MethodTypes = typeof methodTypes[number] | null
56
56
 
57
57
  function isMethod(method: unknown) {
58
- if (typeof method === "string" && methodTypes.includes(<any>method)) {
59
- return method as MethodTypes
60
- }
61
- return null
58
+ if (typeof method === "string" && methodTypes.includes(<any>method)) {
59
+ return method as MethodTypes
60
+ }
61
+ return null
62
62
  }
63
63
 
64
64
  let cache = new Map<string, any>()
65
65
  export async function findRoute(url: URL, method: unknown) {
66
- let validMethod: MethodTypes = isMethod(method)
67
- if (validMethod) {
68
- // @ts-ignore
69
- if (!self.sw?.routes) {
70
- console.error("Expecting routes defined with `self.sw.routes`, but found none.")
66
+ let validMethod: MethodTypes = isMethod(method)
67
+ if (validMethod) {
68
+ // @ts-ignore
69
+ if (!self.sw?.routes) {
70
+ console.error("Expecting routes defined with `self.sw.routes`, but found none.")
71
+ return null
72
+ }
73
+ // @ts-ignore
74
+ for (const r of self.sw.routes) {
75
+ // @ts-ignore
76
+ if (r.file
77
+ && (r.route instanceof RegExp && r.route.test(url.pathname)
78
+ || (r.route instanceof Function && r.route(url)))) {
79
+ let file = links?.find(x => x.url === r.file)?.file
80
+ // Load file
81
+ if (!file) {
82
+ console.error(`"${r.file}" not found in links!`)
83
+ return null
84
+ }
85
+ if (!cache.has(file)) {
86
+ let cachedResponse = await cacheResponse(file)
87
+ if (!cacheResponse) {
88
+ console.error(`"${file}" not found in cache!`)
71
89
  return null
90
+ }
91
+ let text = await cachedResponse.text()
92
+ let func = new Function(text)()
93
+ cache.set(file, func)
72
94
  }
73
- // @ts-ignore
74
- for (const r of self.sw.routes) {
75
- // @ts-ignore
76
- if (r.file
77
- && (r.route instanceof RegExp && r.route.test(url.pathname)
78
- || (r.route instanceof Function && r.route(url)))) {
79
- let file = links?.find(x => x.url === r.file)?.file
80
- // Load file
81
- if (!file) {
82
- console.error(`"${r.file}" not found in links!`)
83
- return null
84
- }
85
- if (!cache.has(file)) {
86
- let cachedResponse = await cacheResponse(file)
87
- if (!cacheResponse) {
88
- console.error(`"${file}" not found in cache!`)
89
- return null
90
- }
91
- let text = await cachedResponse.text()
92
- let func = new Function(text)()
93
- cache.set(file, func)
94
- }
95
-
96
- let routeDef = cache.get(file)
97
- if (routeDef[validMethod]) {
98
- return routeDef[validMethod]
99
- }
100
- }
101
-
102
- if (r[validMethod]
103
- && (r.route instanceof RegExp && r.route.test(url.pathname)
104
- || (r.route instanceof Function && r.route(url)))) {
105
- return r[validMethod]
106
- }
95
+
96
+ let routeDef = cache.get(file)
97
+ if (routeDef[validMethod]) {
98
+ return routeDef[validMethod]
107
99
  }
100
+ }
101
+
102
+ if (r[validMethod]
103
+ && (r.route instanceof RegExp && r.route.test(url.pathname)
104
+ || (r.route instanceof Function && r.route(url)))) {
105
+ return r[validMethod]
106
+ }
108
107
  }
109
- return null
108
+ }
109
+ return null
110
110
  }
111
111
 
112
112
  interface ExectuteHandlerOptions {
113
- url: URL
114
- req: Request
115
- ctx: any
113
+ url: URL
114
+ req: Request
115
+ ctx: any
116
116
  }
117
117
  async function executeHandler({ url, req, ctx }: ExectuteHandlerOptions): Promise<SwResponse> {
118
- let method = req.method.toLowerCase()
119
- let isPost = method === "post"
120
- if (!isPost) {
121
- if (!url.pathname.endsWith("/")) {
122
- return { response: await cacheResponse(url.pathname, req) }
123
- }
118
+ let method = req.method.toLowerCase()
119
+ let isPost = method === "post"
120
+ if (!isPost) {
121
+ if (!url.pathname.endsWith("/")) {
122
+ return { response: await cacheResponse(url.pathname, req) }
123
+ }
124
124
 
125
- if (url.searchParams.get("login") === "success") {
126
- await globalDb.setLoggedIn(true)
127
- }
125
+ if (url.searchParams.get("login") === "success") {
126
+ await globalDb.setLoggedIn(true)
128
127
  }
128
+ }
129
129
 
130
- let handlers =
131
- <RouteHandler<RouteGetArgs | RoutePostArgs> | null>
132
- await findRoute(url, method)
130
+ let handlers =
131
+ <RouteHandler<RouteGetArgs | RoutePostArgs> | null>
132
+ await findRoute(url, method)
133
133
 
134
- // @ts-ignore
135
- if (handlers) {
136
- try {
137
- let messages: string[] = []
138
- const data = await getData(req)
139
- let query = searchParams<{ handler?: string }>(url)
140
- let args = { req, data, query }
141
- let result
142
- try {
143
- result = await (
144
- handlers instanceof Function
145
- ? handlers(args)
146
- : (call(handlers[query.handler ?? ""], args)
147
- || call(handlers[method], args)
148
- || Promise.reject("I'm sorry, I didn't understand where to route your request.")))
149
- } catch (e: any) {
150
- if (Array.isArray(e)) {
151
- ctx.messages = e
152
- return {
153
- status: 204,
154
- }
155
- } else {
156
- throw e
157
- }
158
- }
159
-
160
- if (!result) {
161
- return isPost
162
- ? { response: redirect(req) }
163
- : { error: "Not found!" }
164
- }
165
-
166
- if (isPost && result.message == null) {
167
- messages.push("Saved!")
168
- }
169
-
170
- if (result.message?.length > 0) {
171
- messages.push(result.message)
172
- } else if (result.messages?.length > 0) {
173
- messages.push(...result.messages)
174
- }
175
- ctx.messages = result.messages = messages
176
- ctx.events = result.events
177
-
178
- if (isHtml(result)) {
179
- return {
180
- body: result,
181
- }
182
- }
183
-
184
- if (isHtml(result.body)) {
185
- return {
186
- ...result,
187
- type: "text/html",
188
- }
189
- } else {
190
- if ("json" in result) {
191
- result.body = JSON.stringify(result.json)
192
- result.headers = {
193
- ...result.headers,
194
- }
195
- } else if (result.messages?.length > 0) {
196
- return {
197
- ...result,
198
- status: result.status || 204,
199
- }
200
- }
201
- return {
202
- ...result,
203
- type: "application/json"
204
- }
205
- }
206
- } catch (error) {
207
- console.error(`"${method}" error:`, error, "\nURL:", url);
208
- if (isPost) {
209
- return { error: "An error occurred while processing your request." }
210
- } else {
211
- return { error: "Not found!" }
212
- }
134
+ // @ts-ignore
135
+ if (handlers) {
136
+ try {
137
+ let messages: string[] = []
138
+ const data = await getData(req)
139
+ let query = searchParams<{ handler?: string }>(url)
140
+ let args = { req, data, query }
141
+ let result
142
+ try {
143
+ result = await (
144
+ handlers instanceof Function
145
+ ? handlers(args)
146
+ : (call(handlers[query.handler ?? ""], args)
147
+ || call(handlers[method], args)
148
+ || Promise.reject("I'm sorry, I didn't understand where to route your request.")))
149
+ } catch (e: any) {
150
+ if (typeof e === "string") {
151
+ e = [e]
152
+ }
153
+ if ("message" in e) {
154
+ e = [e.message]
155
+ }
156
+ if (Array.isArray(e)) {
157
+ ctx.messages = e
158
+ return {
159
+ status: 204,
160
+ }
161
+ } else {
162
+ throw e
163
+ }
164
+ }
165
+
166
+ if (!result) {
167
+ return isPost
168
+ ? { response: redirect(req) }
169
+ : { error: "Not found!" }
170
+ }
171
+
172
+ if (isPost && result.message == null) {
173
+ messages.push("Saved!")
174
+ }
175
+
176
+ if (result.message?.length > 0) {
177
+ messages.push(result.message)
178
+ } else if (result.messages?.length > 0) {
179
+ messages.push(...result.messages)
180
+ }
181
+ ctx.messages = result.messages = messages
182
+ ctx.events = result.events
183
+
184
+ if (isHtml(result)) {
185
+ return {
186
+ body: result,
213
187
  }
188
+ }
189
+
190
+ if (isHtml(result.body)) {
191
+ return {
192
+ ...result,
193
+ type: "text/html",
194
+ }
195
+ } else {
196
+ if ("json" in result) {
197
+ result.body = JSON.stringify(result.json)
198
+ result.headers = {
199
+ ...result.headers,
200
+ }
201
+ } else if (result.messages?.length > 0) {
202
+ return {
203
+ ...result,
204
+ status: result.status || 204,
205
+ }
206
+ }
207
+ return {
208
+ ...result,
209
+ type: "application/json"
210
+ }
211
+ }
212
+ } catch (error) {
213
+ console.error(`"${method}" error:`, error, "\nURL:", url);
214
+ if (isPost) {
215
+ return { error: "An error occurred while processing your request." }
216
+ } else {
217
+ return { error: "Not found!" }
218
+ }
214
219
  }
220
+ }
215
221
 
216
- return { error: "Not Found!" }
222
+ return { error: "Not Found!" }
217
223
  }
218
224
 
219
225
  async function getData(req: Request) {
220
- let o: any = {}
221
- if (req.headers.get("content-type")?.includes("application/x-www-form-urlencoded")) {
222
- const formData = await req.formData()
223
- for (let [key, val] of formData.entries()) {
224
- if (key.endsWith("[]")) {
225
- key = key.slice(0, -2)
226
- if (key in o) {
227
- o[key].push(val)
228
- } else {
229
- o[key] = [val]
230
- }
231
- } else {
232
- o[key] = val
233
- }
226
+ let o: any = {}
227
+ if (req.headers.get("content-type")?.includes("application/x-www-form-urlencoded")) {
228
+ const formData = await req.formData()
229
+ for (let [key, val] of formData.entries()) {
230
+ if (key.endsWith("[]")) {
231
+ key = key.slice(0, -2)
232
+ if (key in o) {
233
+ o[key].push(val)
234
+ } else {
235
+ o[key] = [val]
234
236
  }
235
- } else if (req.headers.get("Content-Type")?.includes("json")) {
236
- o = await req.json()
237
+ } else {
238
+ o[key] = val
239
+ }
237
240
  }
238
- return o
241
+ } else if (req.headers.get("Content-Type")?.includes("json")) {
242
+ o = await req.json()
243
+ }
244
+ return o
239
245
  }
240
246
 
241
247
  /**
@@ -243,88 +249,88 @@ async function getData(req: Request) {
243
249
  * /my/script.js -> /my/script.js
244
250
  */
245
251
  function normalizeUrl(url: string): URL {
246
- let uri = new URL(url)
247
- let path = uri.pathname
248
- !uri.pathname.endsWith("/") && (uri.pathname = isFile(path) ? path : path + "/")
249
- return uri
252
+ let uri = new URL(url)
253
+ let path = uri.pathname
254
+ !uri.pathname.endsWith("/") && (uri.pathname = isFile(path) ? path : path + "/")
255
+ return uri
250
256
  }
251
257
 
252
258
  function isFile(s: string) {
253
- return s.lastIndexOf("/") < s.lastIndexOf(".")
259
+ return s.lastIndexOf("/") < s.lastIndexOf(".")
254
260
  }
255
261
 
256
262
  async function cacheResponse(url: string, req?: Request | undefined): Promise<Response> {
257
- url = links?.find(x => x.url === url)?.file || url
258
- const match = await caches.match(url)
259
- if (match) return match
260
- const res = await fetch(req || url)
261
- if (!res || res.status !== 200 || res.type !== "basic") return res
262
- const responseToCache = res.clone()
263
- // @ts-ignore
264
- let version: string = self.sw?.version
265
- ?? (console.warn("The version number is not available, expected glboal value `self.sw.version`."), "")
266
- const cache = await caches.open(version)
267
- cache.put(url, responseToCache)
268
- return res
263
+ url = links?.find(x => x.url === url)?.file || url
264
+ const match = await caches.match(url)
265
+ if (match) return match
266
+ const res = await fetch(req || url)
267
+ if (!res || res.status !== 200 || res.type !== "basic") return res
268
+ const responseToCache = res.clone()
269
+ // @ts-ignore
270
+ let version: string = self.sw?.version
271
+ ?? (console.warn("The version number is not available, expected glboal value `self.sw.version`."), "")
272
+ const cache = await caches.open(version)
273
+ cache.put(url, responseToCache)
274
+ return res
269
275
  }
270
276
 
271
277
  export interface RouteGetArgs {
272
- req: Request
273
- query: any
278
+ req: Request
279
+ query: any
274
280
  }
275
281
 
276
282
  export interface RoutePostArgs {
277
- query: any
278
- data: any
279
- req: Request
283
+ query: any
284
+ data: any
285
+ req: Request
280
286
  }
281
287
 
282
288
  export interface RouteObjectReturn {
283
- body?: string | AsyncGenerator | null
284
- status?: number
285
- headers?: any
286
- events?: any
287
- message?: string
288
- messages?: string[]
289
- json?: any
289
+ body?: string | AsyncGenerator | null
290
+ status?: number
291
+ headers?: any
292
+ events?: any
293
+ message?: string
294
+ messages?: string[]
295
+ json?: any
290
296
  }
291
297
 
292
298
  export type RouteHandler<T> = {
293
- (options: T): Promise<
294
- AsyncGenerator
295
- | Response
296
- | RouteObjectReturn
297
- | undefined
298
- | null
299
- | void> | (
300
- AsyncGenerator
301
- | Response
302
- | RouteObjectReturn
303
- | undefined
304
- | null
305
- | void)
299
+ (options: T): Promise<
300
+ AsyncGenerator
301
+ | Response
302
+ | RouteObjectReturn
303
+ | undefined
304
+ | null
305
+ | void> | (
306
+ AsyncGenerator
307
+ | Response
308
+ | RouteObjectReturn
309
+ | undefined
310
+ | null
311
+ | void)
306
312
  }
307
313
 
308
314
  export interface RoutePostHandler {
309
- [handler: string]: RouteHandler<RoutePostArgs>
315
+ [handler: string]: RouteHandler<RoutePostArgs>
310
316
  }
311
317
 
312
318
  export interface RouteGetHandler {
313
- [handler: string]: RouteHandler<RouteGetArgs>
319
+ [handler: string]: RouteHandler<RouteGetArgs>
314
320
  }
315
321
 
316
322
  interface Route_ {
317
- route: RegExp | ((a: URL) => boolean)
318
- file?: string
319
- get?: RouteHandler<RouteGetArgs> | RouteGetHandler
320
- post?: RouteHandler<RoutePostArgs> | RoutePostHandler
323
+ route: RegExp | ((a: URL) => boolean)
324
+ file?: string
325
+ get?: RouteHandler<RouteGetArgs> | RouteGetHandler
326
+ post?: RouteHandler<RoutePostArgs> | RoutePostHandler
321
327
  }
322
328
 
323
329
  type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
324
- Pick<T, Exclude<keyof T, Keys>>
325
- & {
326
- [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
327
- }[Keys]
330
+ Pick<T, Exclude<keyof T, Keys>>
331
+ & {
332
+ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
333
+ }[Keys]
328
334
 
329
335
  export type Route = RequireAtLeastOne<Route_, "file" | "get" | "post">
330
336
  export type RoutePage = Pick<Route_, "get" | "post">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jon49/sw",
3
- "version": "0.15.2",
3
+ "version": "0.15.3",
4
4
  "description": "Packages for MVC service workers.",
5
5
  "type": "module",
6
6
  "files": [