saloe 0.0.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 (45) hide show
  1. package/README.md +19 -0
  2. package/demos/cloudflare-worker-actions/.wrangler/state/v3/cache/miniflare-CacheObject/9f458c07675338a7426a7b81ac4fb1baf92d034efbcaaf4336379640ed744ded.sqlite +0 -0
  3. package/demos/cloudflare-worker-actions/package.json +29 -0
  4. package/demos/cloudflare-worker-actions/public/input.js +5 -0
  5. package/demos/cloudflare-worker-actions/public/load.js +5 -0
  6. package/demos/cloudflare-worker-actions/public/submit.js +6 -0
  7. package/demos/cloudflare-worker-actions/server.js +93 -0
  8. package/demos/cloudflare-worker-actions/vite.config.js +25 -0
  9. package/demos/cloudflare-worker-actions/wrangler.toml +35 -0
  10. package/demos/cloudflare-worker-server/package.json +29 -0
  11. package/demos/cloudflare-worker-server/server.js +75 -0
  12. package/demos/cloudflare-worker-server/vite.config.js +25 -0
  13. package/demos/cloudflare-worker-server/wrangler.toml +32 -0
  14. package/dist/actions.cjs.js +280 -0
  15. package/dist/actions.es.js +280 -0
  16. package/dist/cloudflare-kv.cjs.js +29 -0
  17. package/dist/cloudflare-kv.es.js +29 -0
  18. package/dist/cloudflare-worker.cjs.js +19 -0
  19. package/dist/cloudflare-worker.es.js +19 -0
  20. package/dist/cookie.cjs.js +18 -0
  21. package/dist/cookie.es.js +18 -0
  22. package/dist/html.cjs.js +78 -0
  23. package/dist/html.es.js +78 -0
  24. package/dist/router.cjs.js +51 -0
  25. package/dist/router.es.js +51 -0
  26. package/dist/urlpattern.cjs.js +23 -0
  27. package/dist/urlpattern.es.js +6 -0
  28. package/dist/util.cjs.js +36 -0
  29. package/dist/util.es.js +36 -0
  30. package/dist/vite.cjs.js +102 -0
  31. package/dist/vite.es.js +101 -0
  32. package/dist/worker.cjs.js +43 -0
  33. package/dist/worker.es.js +43 -0
  34. package/package.json +63 -0
  35. package/src/actions.js +284 -0
  36. package/src/cloudflare-kv.js +36 -0
  37. package/src/cloudflare-worker.js +23 -0
  38. package/src/cookie.js +22 -0
  39. package/src/html.js +99 -0
  40. package/src/router.js +61 -0
  41. package/src/urlpattern.js +11 -0
  42. package/src/util.js +44 -0
  43. package/src/vite.js +127 -0
  44. package/src/worker.js +46 -0
  45. package/vite.config.js +36 -0
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # salo
2
+
3
+ `salo` is a toolkit that makes building reactive web apps easy, with `0kb of initial JavaScript` and lightning-fast rendering.
4
+
5
+ It works seamlessly with Cloudflare Workers and Service Workers, and can also be used in Node or other server-side JavaScript environments.
6
+
7
+ **Note:** `salo` is still under development, and version 1.0 is not ready yet. Expect changes and improvements as we work towards the initial release.
8
+
9
+ ## Packages
10
+
11
+ - [`@jaimytacovega/salo/router`](./src/router.js) - Manage web requests using URLPattern for route matching.
12
+
13
+ - [`@jaimytacovega/salo/html`](./src/html.js) - Stream HTML templates and render temporary templates while waiting for asynchronous functions to finish processing the final template.
14
+
15
+ - [`@jaimytacovega/salo/actions`](./src/actions.js) - Add reactivity to HTML nodes with 0kb of initial JavaScript.
16
+
17
+ ## Coming Soon
18
+
19
+ - [`@jaimytacovega/salo/offline`](./src/offline.js) Manage browser cache resources to handle HTML template requests.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "cloudflare-worker-server",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "watch:dev": "ENV=dev vite build --watch",
8
+ "watch:qa": "ENV=qa vite build --watch",
9
+ "watch:prod": "ENV=prod vite build --watch",
10
+ "build:dev": "ENV=dev vite build",
11
+ "build:qa": "ENV=qa vite build",
12
+ "build:prod": "ENV=prod vite build",
13
+ "local:dev": "wrangler dev --env=dev",
14
+ "local:qa": "wrangler dev --env=qa",
15
+ "local:prod": "wrangler dev --env=prod",
16
+ "deploy:dev": "npm run build:dev && wrangler deploy --env=dev",
17
+ "deploy:qa": "npm run build:qa && wrangler deploy --env=qa",
18
+ "deploy:prod": "npm run build:prod && wrangler deploy --env=prod",
19
+ "build-worker:dev": "wrangler deploy --dry-run --outdir=dist-worker"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@cloudflare/kv-asset-handler": "^0.3.4",
26
+ "@jaimytacovega/salo": "^0.0.28",
27
+ "vite": "^5.4.2"
28
+ }
29
+ }
@@ -0,0 +1,5 @@
1
+ const input = ({ e, srcElement }) => {
2
+ console.log('--- value =', srcElement?.value)
3
+ }
4
+
5
+ export { input }
@@ -0,0 +1,5 @@
1
+ const load = ({ e, srcElement }) => {
2
+ console.log('--- load')
3
+ }
4
+
5
+ export { load }
@@ -0,0 +1,6 @@
1
+ const submit = ({ e, srcElement }) => {
2
+ e.preventDefault()
3
+ console.log('--- submit 2')
4
+ }
5
+
6
+ export { submit }
@@ -0,0 +1,93 @@
1
+ import { findPatternFromUrl, getRedirectResponse, getForbiddenResponse, getRoute, getNotFoundResponse, getServerOnlyResponse } from '@jaimytacovega/salo/router'
2
+ import { getStaticResponse } from '@jaimytacovega/salo/cloudflare-worker'
3
+ import { addRoute } from '@jaimytacovega/salo/router'
4
+ import { html, stream } from '@jaimytacovega/salo/html'
5
+ import { LISTENER_SCRIPT } from '@jaimytacovega/salo/actions'
6
+
7
+ import manifestJSON from '__STATIC_CONTENT_MANIFEST'
8
+
9
+
10
+ const isRedirectableCallback = ({ pathname }) => {
11
+ return pathname !== '/' && pathname.endsWith('/')
12
+ }
13
+
14
+ const isForbiddenCallback = ({ request }) => {
15
+ const forbiddenURLs = []
16
+ return forbiddenURLs.find((filename) => request?.url?.endsWith(filename))
17
+ }
18
+
19
+ const isServerOnlyCallback = ({ request }) => {
20
+ const serverOnlyURLs = []
21
+ return serverOnlyURLs?.find((filename) => request?.url?.endsWith(filename))
22
+ }
23
+
24
+
25
+ (() => {
26
+ addRoute({
27
+ pathname: '/',
28
+ route: ({ request, env }) => {
29
+ return stream({
30
+ head: () => html`
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ <title>Cloudflare Worker Server</title>
34
+ `,
35
+ body: () => html`
36
+
37
+ <form
38
+ on-submit="submit"
39
+ >
40
+ <h1
41
+ on-load="load"
42
+ >
43
+ Hello world!
44
+ </h1>
45
+ <input
46
+ type="text"
47
+ on-input="input"
48
+ />
49
+ <button type="submit">Submit!</button>
50
+ </form>
51
+ `,
52
+ scripts: () => html`
53
+ ${LISTENER_SCRIPT}
54
+ <script>
55
+ console.log('Hello world!')
56
+ </script>
57
+ `,
58
+ env,
59
+ })
60
+ }
61
+ })
62
+
63
+ })()
64
+
65
+ const handleFetch = async ({ request, env, ctx }) => {
66
+ const url = new URL(request.url)
67
+ const { origin, pathname } = url
68
+
69
+ const pattern = findPatternFromUrl({ url })
70
+
71
+ const redirectResult = getRedirectResponse({ origin, pathname, isRedirectableCallback })
72
+ if (redirectResult?.response) return redirectResult.response
73
+
74
+ const forbiddenResult = getForbiddenResponse({ origin, request, isForbiddenCallback })
75
+ if (forbiddenResult?.response) return forbiddenResult.response
76
+
77
+ const serverOnlyResult = getServerOnlyResponse({ origin, request, isServerOnlyCallback })
78
+ if (serverOnlyResult?.response) return serverOnlyResult.response
79
+
80
+ const route = getRoute({ pathname: pattern?.pathname })
81
+ const routeResult = route ? await route({ request, pattern, env }) : null
82
+ if (routeResult?.response) return routeResult.response
83
+
84
+ const staticResult = await getStaticResponse({ request, waitUntil: ctx.waitUntil.bind(ctx), manifestJSON, env })
85
+ if (staticResult?.response) return staticResult?.response
86
+
87
+ const notFoundResult = await getNotFoundResponse({ request })
88
+ return notFoundResult?.response
89
+ }
90
+
91
+ export default {
92
+ fetch: (request, env, ctx) => handleFetch({ request, env, ctx })
93
+ }
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from 'vite'
2
+
3
+
4
+ export default defineConfig({
5
+ define: {
6
+ __ENV__: `'${process.env.ENV}'`,
7
+ __BUILD_TIME__: `'${new Date().toISOString()}'`,
8
+ __APP_NAME__: `'cloudflare-worker-server'`,
9
+ },
10
+ plugins: [],
11
+ build: {
12
+ outDir: './dist',
13
+ manifest: true,
14
+ emptyOutDir: true,
15
+ minify: false,
16
+ rollupOptions: {
17
+ input: {},
18
+ output: {
19
+ entryFileNames: `[name].js`,
20
+ chunkFileNames: `[name].js`,
21
+ assetFileNames: `[name].[ext]`,
22
+ },
23
+ },
24
+ },
25
+ })
@@ -0,0 +1,35 @@
1
+ name = "cloudflare-worker-actions"
2
+ main = "server.js"
3
+ compatibility_date = "2022-11-01"
4
+ compatibility_flags = ["transformstream_enable_standard_constructor", "streams_enable_constructors"]
5
+ minify = false
6
+ #node_compat = true
7
+
8
+ [site]
9
+ bucket = "./public"
10
+
11
+ [env]
12
+
13
+ [env.prod]
14
+ experimental-local = true
15
+
16
+ [env.prod.vars]
17
+ IS_CLOUDFLARE_WORKER = true
18
+ ENV = "prod"
19
+ APP_NAME = "cloudflare-worker-actions"
20
+
21
+ [env.qa]
22
+ experimental-local = true
23
+
24
+ [env.qa.vars]
25
+ IS_CLOUDFLARE_WORKER = true
26
+ ENV = "qa"
27
+ APP_NAME = "cloudflare-worker-actions"
28
+
29
+ [env.dev]
30
+ experimental-local = true
31
+
32
+ [env.dev.vars]
33
+ IS_CLOUDFLARE_WORKER = true
34
+ ENV = "dev"
35
+ APP_NAME = "cloudflare-worker-actions"
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "cloudflare-worker-server",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "watch:dev": "ENV=dev vite build --watch",
8
+ "watch:qa": "ENV=qa vite build --watch",
9
+ "watch:prod": "ENV=prod vite build --watch",
10
+ "build:dev": "ENV=dev vite build",
11
+ "build:qa": "ENV=qa vite build",
12
+ "build:prod": "ENV=prod vite build",
13
+ "local:dev": "wrangler dev --env=dev",
14
+ "local:qa": "wrangler dev --env=qa",
15
+ "local:prod": "wrangler dev --env=prod",
16
+ "deploy:dev": "npm run build:dev && wrangler deploy --env=dev",
17
+ "deploy:qa": "npm run build:qa && wrangler deploy --env=qa",
18
+ "deploy:prod": "npm run build:prod && wrangler deploy --env=prod",
19
+ "build-worker:dev": "wrangler deploy --dry-run --outdir=dist-worker"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@cloudflare/kv-asset-handler": "^0.3.4",
26
+ "@jaimytacovega/salo": "^0.0.16",
27
+ "vite": "^5.4.2"
28
+ }
29
+ }
@@ -0,0 +1,75 @@
1
+ import { findPatternFromUrl, getRedirectResponse, getForbiddenResponse, getRoute, getNotFoundResponse, getServerOnlyResponse } from '@jaimytacovega/salo/router'
2
+ import { getStaticResponse } from '@jaimytacovega/salo/cloudflare-worker'
3
+ import { addRoute } from '@jaimytacovega/salo/router'
4
+ import { html, stream } from '@jaimytacovega/salo/html'
5
+
6
+
7
+ const isRedirectableCallback = ({ pathname }) => {
8
+ return pathname !== '/' && pathname.endsWith('/')
9
+ }
10
+
11
+ const isForbiddenCallback = ({ request }) => {
12
+ const forbiddenURLs = []
13
+ return forbiddenURLs.find((filename) => request?.url?.endsWith(filename))
14
+ }
15
+
16
+ const isServerOnlyCallback = ({ request }) => {
17
+ const serverOnlyURLs = []
18
+ return serverOnlyURLs?.find((filename) => request?.url?.endsWith(filename))
19
+ }
20
+
21
+
22
+ (() => {
23
+ addRoute({
24
+ pathname: '/',
25
+ route: ({ request, env }) => {
26
+ return stream({
27
+ head: () => html`
28
+ <meta charset="UTF-8">
29
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
+ <title>Cloudflare Worker Server</title>
31
+ `,
32
+ body: () => html`
33
+ <h1>Hello world!</h1>
34
+ `,
35
+ scripts: () => html`
36
+ <script>
37
+ console.log('Hello world!')
38
+ </script>
39
+ `,
40
+ env,
41
+ })
42
+ }
43
+ })
44
+
45
+ })()
46
+
47
+ const handleFetch = async ({ request, env, ctx }) => {
48
+ const url = new URL(request.url)
49
+ const { origin, pathname } = url
50
+
51
+ const pattern = findPatternFromUrl({ url })
52
+
53
+ const redirectResult = getRedirectResponse({ origin, pathname, isRedirectableCallback })
54
+ if (redirectResult?.response) return redirectResult.response
55
+
56
+ const forbiddenResult = getForbiddenResponse({ origin, request, isForbiddenCallback })
57
+ if (forbiddenResult?.response) return forbiddenResult.response
58
+
59
+ const serverOnlyResult = getServerOnlyResponse({ origin, request, isServerOnlyCallback })
60
+ if (serverOnlyResult?.response) return serverOnlyResult.response
61
+
62
+ const route = getRoute({ pathname: pattern?.pathname })
63
+ const routeResult = route ? await route({ request, pattern, env }) : null
64
+ if (routeResult?.response) return routeResult.response
65
+
66
+ const staticResult = await getStaticResponse({ request, waitUntil: ctx.waitUntil.bind(ctx), env })
67
+ if (staticResult?.response) return staticResult?.response
68
+
69
+ const notFoundResult = await getNotFoundResponse({ request })
70
+ return notFoundResult?.response
71
+ }
72
+
73
+ export default {
74
+ fetch: (request, env, ctx) => handleFetch({ request, env, ctx })
75
+ }
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from 'vite'
2
+
3
+
4
+ export default defineConfig({
5
+ define: {
6
+ __ENV__: `'${process.env.ENV}'`,
7
+ __BUILD_TIME__: `'${new Date().toISOString()}'`,
8
+ __APP_NAME__: `'cloudflare-worker-server'`,
9
+ },
10
+ plugins: [],
11
+ build: {
12
+ outDir: './dist',
13
+ manifest: true,
14
+ emptyOutDir: true,
15
+ minify: false,
16
+ rollupOptions: {
17
+ input: {},
18
+ output: {
19
+ entryFileNames: `[name].js`,
20
+ chunkFileNames: `[name].js`,
21
+ assetFileNames: `[name].[ext]`,
22
+ },
23
+ },
24
+ },
25
+ })
@@ -0,0 +1,32 @@
1
+ name = "cloudflare-worker-server"
2
+ main = "server.js"
3
+ compatibility_date = "2022-11-01"
4
+ compatibility_flags = ["transformstream_enable_standard_constructor", "streams_enable_constructors"]
5
+ minify = false
6
+ #node_compat = true
7
+
8
+ [env]
9
+
10
+ [env.prod]
11
+ experimental-local = true
12
+
13
+ [env.prod.vars]
14
+ IS_CLOUDFLARE_WORKER = true
15
+ ENV = "prod"
16
+ APP_NAME = "cloudflare-worker-server"
17
+
18
+ [env.qa]
19
+ experimental-local = true
20
+
21
+ [env.qa.vars]
22
+ IS_CLOUDFLARE_WORKER = true
23
+ ENV = "qa"
24
+ APP_NAME = "cloudflare-worker-server"
25
+
26
+ [env.dev]
27
+ experimental-local = true
28
+
29
+ [env.dev.vars]
30
+ IS_CLOUDFLARE_WORKER = true
31
+ ENV = "dev"
32
+ APP_NAME = "cloudflare-worker-server"
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ var __freeze = Object.freeze;
3
+ var __defProp = Object.defineProperty;
4
+ var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
5
+ var _a, _b;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ const html = require("./html.cjs.js");
8
+ const LISTENER_SCRIPT = html.html(_a || (_a = __template([`
9
+ <script defer>
10
+ (() => {
11
+ const EVENTS_PREVENT_DEFAULT_MANDATORY = [
12
+ 'submit'
13
+ ]
14
+
15
+ const EVENTS_FIRE_DOCUMENT_BODY_LISTENERS = [
16
+ 'mouseover',
17
+ 'click',
18
+ ]
19
+
20
+ const EVENTS = [
21
+ 'submit',
22
+ 'input',
23
+ 'blur',
24
+ 'change',
25
+ 'focus',
26
+ 'invalid',
27
+ ]
28
+
29
+
30
+ const addListener = ({ srcElement, event, listeners }) => {
31
+ srcElement?.addEventListener(event, (e) => {
32
+ executeListeners({ e, srcElement, listeners })
33
+ })
34
+ }
35
+
36
+ const executeListeners = ({ e, srcElement, listeners }) => {
37
+ listeners?.forEach((listener) => {
38
+ if (listener) listener({ e, srcElement })
39
+ })
40
+ }
41
+
42
+ const getListenerFromScript = ({ script, event }) => {
43
+ if (!script) return null
44
+ if (script[event]) return script[event]
45
+ const prev = Object.keys(script)?.find((key) => script[key][event])
46
+ if (!prev) return null
47
+ return script[prev][event]
48
+ }
49
+
50
+ const fetchListeners = async ({ srcElement, event, e }) => {
51
+ if (!srcElement?.getAttribute) return
52
+
53
+ const scriptNames = srcElement?.getAttribute('on-' + event)
54
+ if (!scriptNames) return
55
+
56
+ if (scriptNames && EVENTS_PREVENT_DEFAULT_MANDATORY.includes(event)) e.preventDefault()
57
+
58
+ const scripts = await Promise.all(
59
+ scriptNames?.split(',')?.map((scriptName) => {
60
+ const scriptToImport = '/' + scriptName?.trim() + '.js'
61
+ return import(scriptToImport)?.catch((err) => { })
62
+ })
63
+ )
64
+
65
+ const listeners = scripts?.map((script) => getListenerFromScript({ script, event }))
66
+
67
+ return listeners
68
+
69
+ // if (['load', 'click', 'submit', 'input', 'change'].includes(event)) executeListeners({ e, srcElement, listeners })
70
+ // if (['focus', 'blur', 'invalid', 'click', 'submit', 'input', 'change'].includes(event)) addListener({ srcElement, event, listeners })
71
+
72
+ // srcElement?.removeAttribute('on-' + event)
73
+ }
74
+
75
+ const addScripts = () => {
76
+ const scriptsToLoad = [...document.querySelectorAll('script[data-script-to-load]')]
77
+ return Promise.all(
78
+ scriptsToLoad?.map((scriptToLoad) => {
79
+ const id = scriptToLoad?.getAttribute('data-script-to-load')
80
+ scriptToLoad.removeAttribute('data-script-to-load')
81
+
82
+ const attrs = scriptToLoad?.getAttributeNames()?.reduce((acc, attrName) => {
83
+ const attrValue = scriptToLoad.getAttribute(attrName)
84
+ if (attrValue !== 'text/script-to-load') acc[attrName] = attrValue
85
+ return acc
86
+ }, {})
87
+
88
+ const content = scriptToLoad?.textContent
89
+
90
+ scriptToLoad?.remove()
91
+
92
+ return loadScript({ id, attrs, content }).catch((err) => {
93
+ console.error(err)
94
+ })
95
+ })
96
+ )
97
+ }
98
+
99
+ const loadScript = ({ id, attrs, content }) => {
100
+ const script = document?.createElement('script')
101
+
102
+ Object.keys(attrs)?.forEach((attrKey) => script?.setAttribute(attrKey, attrs[attrKey]))
103
+ script.id = id
104
+
105
+ if (content) script?.insertAdjacentHTML('beforeend', content)
106
+
107
+ return new Promise((resolve, reject) => {
108
+ if (!attrs.src) {
109
+ resolve()
110
+ document?.body?.insertAdjacentElement('beforeend', script)
111
+ return
112
+ }
113
+
114
+ script.onload = script.onreadystatechange = function () {
115
+ if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
116
+ resolve()
117
+ script.onload = script.onreadystatechange = null
118
+ }
119
+ }
120
+
121
+ script.onerror = () => {
122
+ console.error('script failed to load')
123
+ reject(new Error('Failed to load script with src ' + script.src))
124
+ }
125
+
126
+ document?.body?.insertAdjacentElement('beforeend', script)
127
+ })
128
+ }
129
+
130
+ // load
131
+ const fireLoadListener = () => {
132
+ const event = 'load'
133
+ const srcElements = document?.querySelectorAll('[on-' + event + ']')
134
+
135
+ srcElements?.forEach(async (srcElement) => {
136
+ const listeners = await fetchListeners({ srcElement, event, e: null })
137
+ executeListeners({ e: null, srcElement, listeners })
138
+
139
+ srcElement?.removeAttribute('on-' + event)
140
+ })
141
+ }
142
+
143
+ // invalid
144
+ const fireInvalidListener = () => {
145
+ const event = 'invalid'
146
+ const srcElements = document?.querySelectorAll('[on-' + event + ']')
147
+
148
+ srcElements?.forEach(async (srcElement) => {
149
+ const listeners = await fetchListeners({ srcElement, event, e: null })
150
+ addListener({ srcElement, event, listeners })
151
+ })
152
+ }
153
+
154
+ // blur
155
+ const fireBlurListener = () => {
156
+ const event = 'blur'
157
+ const srcElements = document?.querySelectorAll('[on-' + event + ']')
158
+
159
+ srcElements?.forEach(async (srcElement) => {
160
+ const listeners = await fetchListeners({ srcElement, event, e: null })
161
+ addListener({ srcElement, event, listeners })
162
+ })
163
+ }
164
+
165
+ // focus
166
+ const fireFocusListener = () => {
167
+ const event = 'focus'
168
+ const srcElements = document?.querySelectorAll('[on-' + event + ']')
169
+
170
+ srcElements?.forEach(async (srcElement) => {
171
+ const listeners = await fetchListeners({ srcElement, event, e: null })
172
+ addListener({ srcElement, event, listeners })
173
+ })
174
+ }
175
+
176
+ // observers
177
+ const fireObserverListeners = () => {
178
+ const srcElements = [...document.querySelectorAll('[on-observe]')]
179
+
180
+ const uniqueScriptNames = [...srcElements?.reduce((acc, srcElement) => {
181
+ const attribute = srcElement?.getAttribute('on-observe')
182
+ if (attribute === 'undefined') return acc
183
+
184
+ const scriptNames = attribute?.split(',')
185
+ scriptNames?.forEach((scriptName) => acc?.set(scriptName, 1))
186
+
187
+ return acc
188
+ }, new Map())?.keys()]
189
+
190
+ uniqueScriptNames?.forEach(async (scriptName) => {
191
+ const observedSrcElements = document.querySelectorAll('[on-observe*="' + scriptName + '"]')
192
+
193
+ const script = await import('/' + scriptName?.trim() + '.js')?.catch((err) => { })
194
+ const listener = getListenerFromScript({ script, event: 'observe' })
195
+ if (!listener) return
196
+
197
+ const observer = new IntersectionObserver((entries) => {
198
+ entries.forEach((entry) => listener({ entry, observer }))
199
+ })
200
+
201
+ observedSrcElements?.forEach((observerSrcElement) => {
202
+ observer.observe(observerSrcElement)
203
+
204
+ const observerAttr = observerSrcElement?.getAttribute('on-observe')
205
+ const updatedObserverAttr = observerAttr?.replaceAll(scriptName + ', ', '')?.replaceAll(', ' + scriptName, '')?.replaceAll(scriptName, '')
206
+
207
+ if (updatedObserverAttr === '') observerSrcElement.removeAttribute('on-observe')
208
+ else observerSrcElement.setAttribute('on-observe', updatedObserverAttr)
209
+ })
210
+ })
211
+ }
212
+
213
+ const getSrcElement = ({ srcElement, event }) => {
214
+ if (!srcElement?.hasAttribute) return srcElement
215
+ const attribute = 'on-' + event
216
+ const hasScriptName = srcElement?.hasAttribute(attribute)
217
+ if (hasScriptName) return srcElement
218
+
219
+ const query = ':is(a, button, li)[' + attribute + ']'
220
+ const closestButton = srcElement?.closest(query)
221
+ if (closestButton) return closestButton
222
+
223
+ return srcElement
224
+ }
225
+
226
+ const fireListeners = () => {
227
+ EVENTS_FIRE_DOCUMENT_BODY_LISTENERS?.forEach((event) => {
228
+ document.body['on' + event] = async (e) => {
229
+ await addScripts()
230
+
231
+ fireLoadListener()
232
+ fireObserverListeners()
233
+ }
234
+ })
235
+
236
+ EVENTS?.forEach((event) => {
237
+ document.body['on' + event] = async (e) => {
238
+ const srcElement = getSrcElement({ srcElement: e?.srcElement, event })
239
+ const listeners = await fetchListeners({ srcElement, event, e })
240
+
241
+ executeListeners({ e, srcElement, listeners })
242
+ addListener({ srcElement, event, listeners })
243
+
244
+ console.log('--- removeAttribute =', srcElement?.removeAttribute)
245
+ if (srcElement?.removeAttribute) srcElement.removeAttribute('on-' + event)
246
+ }
247
+ })
248
+
249
+ // ['mouseover', 'click', 'submit', 'input', 'blur', 'change']?.forEach((event) => {
250
+ // document.body['on' + event] = async (e) => {
251
+ // if (EVENTS_FIRE_DOCUMENT_BODY_LISTENERS.includes(event)) {
252
+ // await addScripts()
253
+
254
+ // fireLoadListener()
255
+ // fireInvalidListener()
256
+ // fireBlurListener()
257
+ // fireFocusListener()
258
+
259
+ // fireObserverListeners()
260
+ // }
261
+
262
+ // const srcElement = getSrcElement({ srcElement: e.srcElement, event })
263
+ // fetchListeners({ srcElement, event, e })
264
+ // }
265
+ // })
266
+ }
267
+
268
+ fireListeners()
269
+
270
+ window.onload = () => {
271
+ setTimeout(() => {
272
+ document?.body?.click()
273
+ }, 2_500)
274
+ }
275
+ })()
276
+ <\/script>
277
+ `])));
278
+ const SW_REGISTER_SCRIPT = html.html(_b || (_b = __template(["\n <script defer>\n (async () => {\n if (!navigator.serviceWorker) return\n\n navigator.serviceWorker.register('/sw.worker.js', { scope: '/', type: 'module' })\n\n let refreshing\n // check to see if there is a current active service worker\n const oldSw = (await navigator.serviceWorker.getRegistration())?.active?.state\n navigator.serviceWorker.addEventListener('controllerchange', async () => {\n if (refreshing) return\n // when the controllerchange event has fired, we get the new service worker\n const newSw = (await navigator.serviceWorker.getRegistration())?.active?.state\n \n // if there was already an old activated service worker, and a new activating service worker, do notify update\n if (oldSw === 'activated' && newSw === 'activating') {\n refreshing = true\n // notifyUpdate()\n location.reload()\n }\n })\n })()\n <\/script> \n"])));
279
+ exports.LISTENER_SCRIPT = LISTENER_SCRIPT;
280
+ exports.SW_REGISTER_SCRIPT = SW_REGISTER_SCRIPT;