@jon49/sw 0.15.1 → 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.
@@ -25,7 +25,7 @@ async function setPagesToReturn(targetDirectory: string) {
25
25
  await Promise.all(files.map(async (file: string) => {
26
26
  let filename = path.join(targetDirectory, file)
27
27
  let content = await readFile(filename, "utf-8")
28
- let matched = content.match(/,(\w*)=(\w*);export/)
28
+ let matched = content.match(/,(\w*)=([\w|\$]*);export/)
29
29
  if (matched) {
30
30
  let [, name, value] = matched
31
31
  content = content.replace(`,${name}=${value}`, "")
@@ -2,241 +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 (e.reasons) {
151
- ctx.messages =
152
- e.reasons.map((x: any) => x.reason.reasons).flat().map((x: any) => x.reason).flat()
153
- return {
154
- status: 204,
155
- }
156
- } else {
157
- throw e
158
- }
159
- }
160
-
161
- if (!result) {
162
- return isPost
163
- ? { response: redirect(req) }
164
- : { error: "Not found!" }
165
- }
166
-
167
- if (isPost && result.message == null) {
168
- messages.push("Saved!")
169
- }
170
-
171
- if (result.message?.length > 0) {
172
- messages.push(result.message)
173
- } else if (result.messages?.length > 0) {
174
- messages.push(...result.messages)
175
- }
176
- ctx.messages = result.messages = messages
177
- ctx.events = result.events
178
-
179
- if (isHtml(result)) {
180
- return {
181
- body: result,
182
- }
183
- }
184
-
185
- if (isHtml(result.body)) {
186
- return {
187
- ...result,
188
- type: "text/html",
189
- }
190
- } else {
191
- if ("json" in result) {
192
- result.body = JSON.stringify(result.json)
193
- result.headers = {
194
- ...result.headers,
195
- }
196
- } else if (result.messages?.length > 0) {
197
- return {
198
- ...result,
199
- status: result.status || 204,
200
- }
201
- }
202
- return {
203
- ...result,
204
- type: "application/json"
205
- }
206
- }
207
- } catch (error) {
208
- console.error(`"${method}" error:`, error, "\nURL:", url);
209
- if (isPost) {
210
- return { error: "An error occurred while processing your request." }
211
- } else {
212
- return { error: "Not found!" }
213
- }
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,
214
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
+ }
215
219
  }
220
+ }
216
221
 
217
- return { error: "Not Found!" }
222
+ return { error: "Not Found!" }
218
223
  }
219
224
 
220
225
  async function getData(req: Request) {
221
- let o: any = {}
222
- if (req.headers.get("content-type")?.includes("application/x-www-form-urlencoded")) {
223
- const formData = await req.formData()
224
- for (let [key, val] of formData.entries()) {
225
- if (key.endsWith("[]")) {
226
- key = key.slice(0, -2)
227
- if (key in o) {
228
- o[key].push(val)
229
- } else {
230
- o[key] = [val]
231
- }
232
- } else {
233
- o[key] = val
234
- }
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]
235
236
  }
236
- } else if (req.headers.get("Content-Type")?.includes("json")) {
237
- o = await req.json()
237
+ } else {
238
+ o[key] = val
239
+ }
238
240
  }
239
- return o
241
+ } else if (req.headers.get("Content-Type")?.includes("json")) {
242
+ o = await req.json()
243
+ }
244
+ return o
240
245
  }
241
246
 
242
247
  /**
@@ -244,88 +249,88 @@ async function getData(req: Request) {
244
249
  * /my/script.js -> /my/script.js
245
250
  */
246
251
  function normalizeUrl(url: string): URL {
247
- let uri = new URL(url)
248
- let path = uri.pathname
249
- !uri.pathname.endsWith("/") && (uri.pathname = isFile(path) ? path : path + "/")
250
- 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
251
256
  }
252
257
 
253
258
  function isFile(s: string) {
254
- return s.lastIndexOf("/") < s.lastIndexOf(".")
259
+ return s.lastIndexOf("/") < s.lastIndexOf(".")
255
260
  }
256
261
 
257
262
  async function cacheResponse(url: string, req?: Request | undefined): Promise<Response> {
258
- url = links?.find(x => x.url === url)?.file || url
259
- const match = await caches.match(url)
260
- if (match) return match
261
- const res = await fetch(req || url)
262
- if (!res || res.status !== 200 || res.type !== "basic") return res
263
- const responseToCache = res.clone()
264
- // @ts-ignore
265
- let version: string = self.sw?.version
266
- ?? (console.warn("The version number is not available, expected glboal value `self.sw.version`."), "")
267
- const cache = await caches.open(version)
268
- cache.put(url, responseToCache)
269
- 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
270
275
  }
271
276
 
272
277
  export interface RouteGetArgs {
273
- req: Request
274
- query: any
278
+ req: Request
279
+ query: any
275
280
  }
276
281
 
277
282
  export interface RoutePostArgs {
278
- query: any
279
- data: any
280
- req: Request
283
+ query: any
284
+ data: any
285
+ req: Request
281
286
  }
282
287
 
283
288
  export interface RouteObjectReturn {
284
- body?: string | AsyncGenerator | null
285
- status?: number
286
- headers?: any
287
- events?: any
288
- message?: string
289
- messages?: string[]
290
- 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
291
296
  }
292
297
 
293
298
  export type RouteHandler<T> = {
294
- (options: T): Promise<
295
- AsyncGenerator
296
- | Response
297
- | RouteObjectReturn
298
- | undefined
299
- | null
300
- | void> | (
301
- AsyncGenerator
302
- | Response
303
- | RouteObjectReturn
304
- | undefined
305
- | null
306
- | 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)
307
312
  }
308
313
 
309
314
  export interface RoutePostHandler {
310
- [handler: string]: RouteHandler<RoutePostArgs>
315
+ [handler: string]: RouteHandler<RoutePostArgs>
311
316
  }
312
317
 
313
318
  export interface RouteGetHandler {
314
- [handler: string]: RouteHandler<RouteGetArgs>
319
+ [handler: string]: RouteHandler<RouteGetArgs>
315
320
  }
316
321
 
317
322
  interface Route_ {
318
- route: RegExp | ((a: URL) => boolean)
319
- file?: string
320
- get?: RouteHandler<RouteGetArgs> | RouteGetHandler
321
- post?: RouteHandler<RoutePostArgs> | RoutePostHandler
323
+ route: RegExp | ((a: URL) => boolean)
324
+ file?: string
325
+ get?: RouteHandler<RouteGetArgs> | RouteGetHandler
326
+ post?: RouteHandler<RoutePostArgs> | RoutePostHandler
322
327
  }
323
328
 
324
329
  type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
325
- Pick<T, Exclude<keyof T, Keys>>
326
- & {
327
- [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
328
- }[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]
329
334
 
330
335
  export type Route = RequireAtLeastOne<Route_, "file" | "get" | "post">
331
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.1",
3
+ "version": "0.15.3",
4
4
  "description": "Packages for MVC service workers.",
5
5
  "type": "module",
6
6
  "files": [