@fullstackunicorn/create-root 1.0.26 → 1.0.28
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.
- package/assets/root.fullstackunicorn.dev/assets/config.cmd.sh +2 -2
- package/assets/root.fullstackunicorn.dev/assets/package.md +26 -0
- package/assets/root.fullstackunicorn.dev/backend/modules/loader.js +1 -0
- package/assets/root.fullstackunicorn.dev/backend/modules/pulse.js +22 -0
- package/assets/root.fullstackunicorn.dev/backend/server/router.js +10 -0
- package/assets/root.fullstackunicorn.dev/frontend/assets/index.webmanifest +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/alerts/Alert.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/callout/Callout.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/Header.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/Layout.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/sideTwo/IconTwo.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/sideTwo/SidebarTwo.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/modals/Modal.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/modals/ModalPopup.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/client/statics.js +3 -2
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/Button.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/Crossfader.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/HoverCard.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/client.js +22 -10
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Img.js +62 -10
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Img.pod.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Scrollable.js +10 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Text.js +11 -2
- package/assets/root.fullstackunicorn.dev/frontend/imports/service.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/index.html +5 -5
- package/assets/root.fullstackunicorn.dev/frontend/modules/base/ComingSoon.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/modules/base/PageTitle.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/layout/Wrapper.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/modals/select/SelectItem.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/modals/select/SelectPicked.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/panels/slideshows/Slideshow.pod.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/templates/cards.js +9 -9
- package/assets/root.fullstackunicorn.dev/frontend/templates/page.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/templates/root.js +1 -1
- package/assets/root.fullstackunicorn.dev/manager/modules/pulse.js +22 -0
- package/assets/root.fullstackunicorn.dev/manager/server/router.js +11 -1
- package/assets/root.fullstackunicorn.dev/workspace/config/workers.json +14 -2
- package/index.cmd.help.js +33 -0
- package/index.cmd.js +20 -3
- package/package.json +2 -1
|
@@ -17,7 +17,7 @@ add() {
|
|
|
17
17
|
yarn add axios --latest
|
|
18
18
|
yarn add blurhash --latest
|
|
19
19
|
yarn add jszip --latest
|
|
20
|
-
yarn add sharp
|
|
20
|
+
yarn add sharp@0.34.5
|
|
21
21
|
|
|
22
22
|
echo "🚀 [assets] Adding Dev Dependencies"
|
|
23
23
|
|
|
@@ -83,4 +83,4 @@ if declare -F "$cmd" >/dev/null && [[ "$cmd" != "info" ]]; then
|
|
|
83
83
|
else
|
|
84
84
|
info "$cmd"
|
|
85
85
|
fi
|
|
86
|
-
# ============================================================
|
|
86
|
+
# ============================================================
|
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
Create-root local flow:
|
|
2
|
+
|
|
3
|
+
```bash
|
|
1
4
|
yarn cmd website local-push
|
|
2
5
|
yarn cmd website local-assets
|
|
3
6
|
yarn cmd website local-workers
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Other supported commands:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
yarn cmd website workspace-cleanup
|
|
13
|
+
yarn cmd website develop-cleanup
|
|
14
|
+
|
|
15
|
+
yarn cmd website local-config
|
|
16
|
+
yarn cmd website local-pull
|
|
17
|
+
|
|
18
|
+
yarn cmd website sv1-config
|
|
19
|
+
yarn cmd website sv1-push
|
|
20
|
+
yarn cmd website sv1-pull
|
|
21
|
+
yarn cmd website sv1-assets
|
|
22
|
+
yarn cmd website sv1-workers
|
|
23
|
+
|
|
24
|
+
yarn cmd website stage-config
|
|
25
|
+
yarn cmd website stage-push
|
|
26
|
+
yarn cmd website stage-pull
|
|
27
|
+
yarn cmd website stage-assets
|
|
28
|
+
yarn cmd website stage-workers
|
|
29
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class Pulse {
|
|
2
|
+
|
|
3
|
+
get = async () => this.formatDate(new Date())
|
|
4
|
+
|
|
5
|
+
formatDate = date => {
|
|
6
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
7
|
+
weekday: 'short',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
month: 'short',
|
|
10
|
+
year: 'numeric',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
hour12: false,
|
|
14
|
+
timeZone: 'UTC'
|
|
15
|
+
}).formatToParts(date)
|
|
16
|
+
|
|
17
|
+
const value = type => parts.find(part => part.type === type)?.value ?? ''
|
|
18
|
+
return `${value('weekday')} ${value('day')} ${value('month')} ${value('year')} ${value('hour')}:${value('minute')}`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Pulse }
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { Router } from '#@/index.dep.js'
|
|
2
2
|
import { middleware } from '#@/server/middleware.js'
|
|
3
3
|
import { resolver } from '#@/server/resolvers.js'
|
|
4
|
+
import { Pulse } from '#@/modules/pulse.js'
|
|
4
5
|
import { Loader } from '#@/modules/loader.js'
|
|
5
6
|
import { Cookies } from '#@/modules/cookies.js'
|
|
6
7
|
import { Stripe } from '#@/modules/stripe.js'
|
|
7
8
|
|
|
8
9
|
const rs = {}
|
|
9
10
|
|
|
11
|
+
// pulse
|
|
12
|
+
|
|
13
|
+
rs.pulse = {}
|
|
14
|
+
rs.pulse.method = 'get'
|
|
15
|
+
rs.pulse.name = 'pulse'
|
|
16
|
+
rs.pulse.path = '/pulse'
|
|
17
|
+
rs.pulse.resolve = async (...args) => await resolver.resolve(new Pulse().get, rs.pulse.path, ...args)
|
|
18
|
+
rs.pulse.funcs = [ rs.pulse.resolve ]
|
|
19
|
+
|
|
10
20
|
// loader
|
|
11
21
|
|
|
12
22
|
rs.index = {}
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
"display": "fullscreen",
|
|
6
6
|
"icons": [
|
|
7
7
|
{
|
|
8
|
-
"src": "/icons/icon-192.png",
|
|
8
|
+
"src": "/get/media/icons/icon-192.png",
|
|
9
9
|
"sizes": "192x192",
|
|
10
10
|
"type": "image/png",
|
|
11
11
|
"purpose": "maskable"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
"src": "/icons/icon-512.png",
|
|
14
|
+
"src": "/get/media/icons/icon-512.png",
|
|
15
15
|
"sizes": "512x512",
|
|
16
16
|
"type": "image/png",
|
|
17
17
|
"purpose": "maskable"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
20
|
"theme_color": "#ffffff"
|
|
21
|
-
}
|
|
21
|
+
}
|
|
@@ -11,8 +11,8 @@ export const AlertPod = ({ close, message, svg, isError }) => {
|
|
|
11
11
|
|
|
12
12
|
B.color = isError ? c.item_RR : c.item_OR
|
|
13
13
|
|
|
14
|
-
S.overlay = [s.row_cXsY, s.
|
|
15
|
-
S.alert = [s.col_cX, s.X330, s.
|
|
14
|
+
S.overlay = [s.row_cXsY, s.positionAbsolute, s.overflowHidden, s.paddingY96, s.paddingX32, s.XY100p, s.G1, s.Z15, c.base_NN_B750_40]
|
|
15
|
+
S.alert = [s.col_cX, s.X330, s.overflowHidden, s.padding32, s.radius20, c.base_NN_W700]
|
|
16
16
|
S.message = [...F.message, s.row_cXcY, s.textC, s.marginY16, c.item_NN_B400]
|
|
17
17
|
S.icon = [s.row_cXcY, s.XY40, B.color]
|
|
18
18
|
|
|
@@ -9,10 +9,10 @@ export const CalloutPod = ({ text, link, linkTo }) => {
|
|
|
9
9
|
|
|
10
10
|
F.text = [s.fs16, s.lh24, s.fw500]
|
|
11
11
|
|
|
12
|
-
// S.alpha = [s.col_cXcY, s.
|
|
13
|
-
S.alpha = [s.col_cXcY, s.Y64, s.
|
|
12
|
+
// S.alpha = [s.col_cXcY, s.positionAbsolute, s.bottom0, s.Y64, s.X100p, c.base_RR]
|
|
13
|
+
S.alpha = [s.col_cXcY, s.Y64, s.X100p, c.base_RR_B400]
|
|
14
14
|
S.link = [s.row_cXcY, s.Xc, ]
|
|
15
|
-
S.text = [...F.text, s.textC, s.
|
|
15
|
+
S.text = [...F.text, s.textC, s.Xx100p, c.item_NN_W700]
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<View classes={S.alpha}>
|
|
@@ -8,8 +8,8 @@ export const HeaderPod = ({ auth, center, back, logo, svg }) => {
|
|
|
8
8
|
|
|
9
9
|
H.icon = [[c.base_NN_W700]]
|
|
10
10
|
|
|
11
|
-
S.header = [s.col_sY, s.
|
|
12
|
-
S.box = [s.row_cY, s.
|
|
11
|
+
S.header = [s.col_sY, s.X100p, c.base_WW]
|
|
12
|
+
S.box = [s.row_cY, s.X100p, s.Y64]
|
|
13
13
|
S.one = [s.row_sX]
|
|
14
14
|
S.middle = [s.row_cXcY, s.Xr]
|
|
15
15
|
S.two = [s.row_eX]
|
|
@@ -7,9 +7,9 @@ export const LayoutPod = ({ sidebarTwo, iconTwo,
|
|
|
7
7
|
|
|
8
8
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
9
9
|
|
|
10
|
-
S.root = [s.col, s.
|
|
11
|
-
S.layout = [s.col, s.
|
|
12
|
-
S.body = [s.col_cX, s.
|
|
10
|
+
S.root = [s.col, s.X100p, s.Yc, s.Ymin0]
|
|
11
|
+
S.layout = [s.col, s.X100p, s.Yc, s.Ymin0]
|
|
12
|
+
S.body = [s.col_cX, s.X100p, s.Yc, s.Ymin0, s.overflowXHidden, c.base_NN_W750]
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<View classes={S.root} inline={I.root}>
|
|
@@ -11,7 +11,7 @@ export const IconTwoPod = ({ svg, onPress, isOn }) => {
|
|
|
11
11
|
|
|
12
12
|
B.isOn = isOn ? s.Z10 : s.Z1
|
|
13
13
|
|
|
14
|
-
S.iconBox = [s.
|
|
14
|
+
S.iconBox = [s.positionAbsolute, s.row_cXcY, s.X48, s.Y64, s.end0, B.isOn]
|
|
15
15
|
S.icon = [s.XY40, s.marginX4, s.marginY12, s.radius32, c.item_NN_B600]
|
|
16
16
|
|
|
17
17
|
return (
|
|
@@ -8,11 +8,11 @@ export const SidebarTwoPod = ({ isClosed, menu, onPress }) => {
|
|
|
8
8
|
|
|
9
9
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
10
10
|
|
|
11
|
-
S.overlay = [s.col, s.
|
|
11
|
+
S.overlay = [s.col, s.positionAbsolute, s.overflowHidden, s.X100p, s.Y100p, s.Z4, s.flex1]
|
|
12
12
|
S.main = [s.row_sX_r, s.Xc, s.Yc,]
|
|
13
13
|
S.external = [s.col, s.Xmax320, s.G100, s.paddingX8, c.base_WW]
|
|
14
14
|
S.header = [s.col_cY, s.Y64, s.paddingX4, s.Xc, c.base_WW]
|
|
15
|
-
S.bodyExt = [s.col, s.Yc, s.
|
|
15
|
+
S.bodyExt = [s.col, s.Yc, s.X100p, s.paddingX4]
|
|
16
16
|
S.space = [s.row, s.Xmin16, s.XYr]
|
|
17
17
|
|
|
18
18
|
return (
|
|
@@ -15,8 +15,8 @@ export const ModalPod = ({ children, title, svg, actions, isCloseDisabled, style
|
|
|
15
15
|
B.isMax = style.isMax ? [s.Xr, s.Xmax600] : [s.Xr]
|
|
16
16
|
B.margin = style.margin
|
|
17
17
|
|
|
18
|
-
S.overlay = [s.row_cXcY, s.
|
|
19
|
-
S.modal = [s.col, s.Yr, s.Ymin0, s.
|
|
18
|
+
S.overlay = [s.row_cXcY, s.positionAbsolute, s.overflowHidden, s.paddingX16, s.XY100p, s.G1, s.Z10, c.base_NN_B750_40]
|
|
19
|
+
S.modal = [s.col, s.Yr, s.Ymin0, s.overflowHidden, s.paddingX20, s.radius20, s.marginY16, ...B.isMax, ...B.margin, c.base_NN_W700]
|
|
20
20
|
S.layout = [s.col, s.XYc, s.Ymin0]
|
|
21
21
|
S.header = [s.row_cXcY, s.Xr, s.Y40, s.marginY20]
|
|
22
22
|
S.icon = [s.XY32, s.margin4, c.item_NN_B400]
|
|
@@ -13,9 +13,9 @@ export const ModalPopupPod = ({ children, title, svg, actions, isCloseDisabled,
|
|
|
13
13
|
|
|
14
14
|
B.disabled = isCloseDisabled ? c.item_NN_B600_30 : c.item_NN_B600
|
|
15
15
|
|
|
16
|
-
S.overlay = [s.row_cXcY, s.
|
|
17
|
-
S.modal = [s.col, s.Xx360, s.Yy360, s.
|
|
18
|
-
S.layout = [s.col, s.
|
|
16
|
+
S.overlay = [s.row_cXcY, s.positionAbsolute, s.overflowHidden, s.paddingX16, s.XY100p, s.G1, s.Z10, c.base_NN_B750_40]
|
|
17
|
+
S.modal = [s.col, s.Xx360, s.Yy360, s.overflowHidden, s.paddingX20, s.radius20, s.marginY16, c.base_NN_W700]
|
|
18
|
+
S.layout = [s.col, s.X100p, s.Y100p]
|
|
19
19
|
S.header = [s.row_cXcY, s.Xr, s.Y40, s.marginY20]
|
|
20
20
|
S.icon = [s.XY32, s.margin4, c.item_NN_B400]
|
|
21
21
|
S.iconCancel = [s.XY32, s.margin4, B.disabled]
|
|
@@ -30,12 +30,13 @@ class Statics {
|
|
|
30
30
|
return response
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
image = async (path, type) => {
|
|
33
|
+
image = async (path, type, config = {}) => {
|
|
34
34
|
|
|
35
35
|
const payload = {}
|
|
36
36
|
payload.method = 'get'
|
|
37
37
|
payload.endpoint = path
|
|
38
38
|
payload.key = abs.makeKey(payload.endpoint, payload.method)
|
|
39
|
+
payload.config = config
|
|
39
40
|
|
|
40
41
|
const response = await client.image(payload, type)
|
|
41
42
|
|
|
@@ -44,4 +45,4 @@ class Statics {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
export const statics = new Statics()
|
|
48
|
+
export const statics = new Statics()
|
|
@@ -56,9 +56,9 @@ export const ControlButton = ({ onPress = () => { }, classes = [], children }) =
|
|
|
56
56
|
onMouseUp={() => setIsPressed(false)}
|
|
57
57
|
onMouseLeave={() => setIsPressed(false)}
|
|
58
58
|
style={containerStyle}
|
|
59
|
-
className={[...classes, s.
|
|
59
|
+
className={[...classes, s.positionRelative].join(' ')}
|
|
60
60
|
>
|
|
61
|
-
<div className={[...classes, s.
|
|
61
|
+
<div className={[...classes, s.positionRelative].join(' ')} style={iconStyle}>
|
|
62
62
|
{children}
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
@@ -13,7 +13,7 @@ export const Crossfader = ({ isActive, children, transitionMs = 600, instantMoun
|
|
|
13
13
|
|
|
14
14
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
15
15
|
|
|
16
|
-
S.box = [s.
|
|
16
|
+
S.box = [s.positionAbsolute, s.XY100p, s.noSelect]
|
|
17
17
|
|
|
18
18
|
I.box = {
|
|
19
19
|
width: '100%',
|
|
@@ -7,7 +7,7 @@ export const HoverCard = ({ children, classes = [] }) => {
|
|
|
7
7
|
|
|
8
8
|
const [isHover, setIsHover] = useState(false)
|
|
9
9
|
|
|
10
|
-
S.box = [s.
|
|
10
|
+
S.box = [s.positionRelative, s.radius24, s.overflowHidden, ...classes]
|
|
11
11
|
|
|
12
12
|
I.box = {
|
|
13
13
|
transition: 'transform 240ms cubic-bezier(0.2, 0, 0, 1), box-shadow 240ms cubic-bezier(0.2, 0, 0, 1)',
|
|
@@ -109,7 +109,7 @@ class Client {
|
|
|
109
109
|
if (isFresh) return cachedResponse
|
|
110
110
|
|
|
111
111
|
config.timeout = 10000
|
|
112
|
-
if (etag) {
|
|
112
|
+
if (etag && cachedItem) {
|
|
113
113
|
if (!config.headers) config.headers = {}
|
|
114
114
|
config.headers['If-None-Match'] = etag
|
|
115
115
|
}
|
|
@@ -121,6 +121,7 @@ class Client {
|
|
|
121
121
|
: await axios[method](endpoint, data, config)
|
|
122
122
|
|
|
123
123
|
if (cachedItem && response.status === 304) {
|
|
124
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
124
125
|
return cachedResponse
|
|
125
126
|
}
|
|
126
127
|
|
|
@@ -139,21 +140,25 @@ class Client {
|
|
|
139
140
|
|
|
140
141
|
const cacheStore = await cache.open(this.cacheNameImg)
|
|
141
142
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
142
|
-
if (!cachedBlob) return null
|
|
143
|
-
return { key, blob: cachedBlob }
|
|
143
|
+
if (!cachedBlob?.size) return null
|
|
144
|
+
return { key, blob: cachedBlob, isCached: true }
|
|
144
145
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
cacheImgSet = async ({ key, endpoint, config = {} }) => {
|
|
148
149
|
const cacheStore = await cache.open(this.cacheNameImg)
|
|
149
150
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
150
|
-
const cachedResponse = cachedBlob ? { key, blob: cachedBlob } : undefined
|
|
151
|
+
const cachedResponse = cachedBlob?.size ? { key, blob: cachedBlob, isCached: true } : undefined
|
|
151
152
|
const { etag, timestamp } = await cache.getMeta(key)
|
|
152
153
|
|
|
154
|
+
const age = (Date.now() - timestamp) / 1000
|
|
155
|
+
const isFresh = cachedResponse && age < this.cacheStale
|
|
156
|
+
if (isFresh) return { ...cachedResponse, isFresh: true }
|
|
157
|
+
|
|
153
158
|
config.responseType = 'arraybuffer'
|
|
154
159
|
config.timeout = 10000
|
|
155
160
|
|
|
156
|
-
if (etag) {
|
|
161
|
+
if (etag && cachedResponse) {
|
|
157
162
|
if (!config.headers) config.headers = {}
|
|
158
163
|
config.headers['If-None-Match'] = etag
|
|
159
164
|
}
|
|
@@ -161,15 +166,19 @@ class Client {
|
|
|
161
166
|
try {
|
|
162
167
|
const response = await axios.get(endpoint, config)
|
|
163
168
|
|
|
164
|
-
if (
|
|
169
|
+
if (cachedResponse && response.status === 304) {
|
|
170
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
171
|
+
return { ...cachedResponse, isRevalidated: true }
|
|
172
|
+
}
|
|
165
173
|
|
|
166
174
|
const blob = cache.getBlob(response.data, response.headers['content-type'])
|
|
167
175
|
await cache.set(cacheStore, key, blob, true)
|
|
168
176
|
await cache.setMeta(key, response.headers['etag'])
|
|
169
177
|
|
|
170
|
-
return { key, blob }
|
|
178
|
+
return { key, blob, isUpdated: true }
|
|
171
179
|
|
|
172
180
|
} catch (error) {
|
|
181
|
+
if (error?.code === 'ERR_CANCELED' || config.signal?.aborted) return cachedResponse || { isCanceled: true }
|
|
173
182
|
return await err.cache(error, cachedResponse)
|
|
174
183
|
}
|
|
175
184
|
}
|
|
@@ -178,7 +187,7 @@ class Client {
|
|
|
178
187
|
|
|
179
188
|
const cacheStore = await cache.open(this.cacheNameFiles)
|
|
180
189
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
181
|
-
const cachedResponse = cachedBlob ? { key, blob: cachedBlob } : undefined
|
|
190
|
+
const cachedResponse = cachedBlob?.size ? { key, blob: cachedBlob } : undefined
|
|
182
191
|
const { etag, timestamp } = await cache.getMeta(key)
|
|
183
192
|
|
|
184
193
|
const age = (Date.now() - timestamp) / 1000
|
|
@@ -188,7 +197,7 @@ class Client {
|
|
|
188
197
|
config.responseType = 'arraybuffer'
|
|
189
198
|
config.timeout = 10000
|
|
190
199
|
|
|
191
|
-
if (etag) {
|
|
200
|
+
if (etag && cachedResponse) {
|
|
192
201
|
if (!config.headers) config.headers = {}
|
|
193
202
|
config.headers['If-None-Match'] = etag
|
|
194
203
|
}
|
|
@@ -198,7 +207,10 @@ class Client {
|
|
|
198
207
|
? await axios.get(endpoint, config)
|
|
199
208
|
: await axios[method](endpoint, data, config)
|
|
200
209
|
|
|
201
|
-
if (
|
|
210
|
+
if (cachedResponse && response.status === 304) {
|
|
211
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
212
|
+
return cachedResponse
|
|
213
|
+
}
|
|
202
214
|
|
|
203
215
|
const blob = cache.getBlob(response.data, response.headers['content-type'])
|
|
204
216
|
await cache.set(cacheStore, key, blob, true)
|
|
@@ -6,6 +6,34 @@ import { api } from '#@/client/@api.js'
|
|
|
6
6
|
|
|
7
7
|
export const ImgData = (props) => <ImgDataPod {...props} />
|
|
8
8
|
|
|
9
|
+
const inFlightImages = new Map()
|
|
10
|
+
|
|
11
|
+
const retainImageRequest = (src) => {
|
|
12
|
+
let entry = inFlightImages.get(src)
|
|
13
|
+
|
|
14
|
+
if (!entry) {
|
|
15
|
+
const controller = new AbortController()
|
|
16
|
+
const promise = api.statics.image(src, 'set', { signal: controller.signal })
|
|
17
|
+
.finally(() => inFlightImages.delete(src))
|
|
18
|
+
|
|
19
|
+
entry = { controller, promise, retainers: 0 }
|
|
20
|
+
inFlightImages.set(src, entry)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let released = false
|
|
24
|
+
entry.retainers += 1
|
|
25
|
+
|
|
26
|
+
const release = () => {
|
|
27
|
+
if (released) return
|
|
28
|
+
released = true
|
|
29
|
+
|
|
30
|
+
entry.retainers = Math.max(0, entry.retainers - 1)
|
|
31
|
+
if (entry.retainers === 0) entry.controller.abort()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { promise: entry.promise, release }
|
|
35
|
+
}
|
|
36
|
+
|
|
9
37
|
export const NoImg = (props) => {
|
|
10
38
|
const statics = useGetStatics()
|
|
11
39
|
return <NoImgPod {...props} svg={statics.visual.svg.picture} />
|
|
@@ -19,9 +47,12 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
19
47
|
const [isSlow, setIsSlow] = useState(false)
|
|
20
48
|
|
|
21
49
|
useEffect(() => {
|
|
50
|
+
if (image.isNoImage || !image.src) return
|
|
51
|
+
|
|
22
52
|
let mounted = true
|
|
23
53
|
let objectUrl = null
|
|
24
54
|
let slowTimer = null
|
|
55
|
+
let releaseRequest = null
|
|
25
56
|
|
|
26
57
|
setSrc(undefined)
|
|
27
58
|
setIsLoaded(false)
|
|
@@ -31,25 +62,45 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
31
62
|
if (mounted) setIsSlow(true)
|
|
32
63
|
}, 150)
|
|
33
64
|
|
|
65
|
+
const setBlob = (blob) => {
|
|
66
|
+
clearTimeout(slowTimer)
|
|
67
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
68
|
+
objectUrl = URL.createObjectURL(blob)
|
|
69
|
+
setSrc(objectUrl)
|
|
70
|
+
setIsLoaded(true)
|
|
71
|
+
setIsSlow(false)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const fetchImage = async ({ updateOnly = false } = {}) => {
|
|
75
|
+
const request = retainImageRequest(image.src)
|
|
76
|
+
releaseRequest = request.release
|
|
77
|
+
|
|
78
|
+
const fetched = await request.promise
|
|
79
|
+
releaseRequest()
|
|
80
|
+
releaseRequest = null
|
|
81
|
+
|
|
82
|
+
if (fetched?.isCanceled) return fetched
|
|
83
|
+
if (!mounted) return fetched
|
|
84
|
+
if (!fetched?.blob) return fetched
|
|
85
|
+
if (updateOnly && !fetched.isUpdated) return fetched
|
|
86
|
+
|
|
87
|
+
setBlob(fetched.blob)
|
|
88
|
+
return fetched
|
|
89
|
+
}
|
|
90
|
+
|
|
34
91
|
const load = async () => {
|
|
35
92
|
const cached = await api.statics.image(image.src, 'get')
|
|
36
93
|
|
|
37
94
|
if (mounted && cached?.blob) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
setSrc(objectUrl)
|
|
41
|
-
setIsLoaded(true)
|
|
42
|
-
setIsSlow(false)
|
|
95
|
+
setBlob(cached.blob)
|
|
96
|
+
fetchImage({ updateOnly: true })
|
|
43
97
|
return
|
|
44
98
|
}
|
|
45
99
|
|
|
46
|
-
const fetched = await
|
|
100
|
+
const fetched = await fetchImage()
|
|
101
|
+
if (fetched?.isCanceled) return
|
|
47
102
|
|
|
48
103
|
if (mounted && fetched?.blob) {
|
|
49
|
-
clearTimeout(slowTimer)
|
|
50
|
-
objectUrl = URL.createObjectURL(fetched.blob)
|
|
51
|
-
setSrc(objectUrl)
|
|
52
|
-
setIsLoaded(true)
|
|
53
104
|
return
|
|
54
105
|
}
|
|
55
106
|
|
|
@@ -68,6 +119,7 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
68
119
|
return () => {
|
|
69
120
|
mounted = false
|
|
70
121
|
clearTimeout(slowTimer)
|
|
122
|
+
if (releaseRequest) releaseRequest()
|
|
71
123
|
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
72
124
|
}
|
|
73
125
|
}, [image.src])
|
|
@@ -17,7 +17,7 @@ export const NoImgPod = ({ classes, inline, svg }) => {
|
|
|
17
17
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
18
18
|
|
|
19
19
|
S.box = [...classes, c.base_NN_W700]
|
|
20
|
-
S.svg = [s.
|
|
20
|
+
S.svg = [s.XY100p, c.item_NN_W750]
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
23
|
<View classes={S.box} inline={inline}>
|
|
@@ -37,9 +37,9 @@ export const ImgPod = ({
|
|
|
37
37
|
B.imgTransition = isSlow ? { transition: '0.3s all ease-in-out', ...inline } : { ...inline }
|
|
38
38
|
B.blurhashTransition = { transition: '0.3s all ease-in-out' }
|
|
39
39
|
|
|
40
|
-
S.image = [s.
|
|
41
|
-
S.blurhash = [s.
|
|
42
|
-
S.box = [s.
|
|
40
|
+
S.image = [s.positionRelative, s.overflowHidden, s.row_cXcY, ...classes]
|
|
41
|
+
S.blurhash = [s.positionAbsolute, s.XY100p, ...B.blurhashOpacity]
|
|
42
|
+
S.box = [s.positionAbsolute, s.XY100p, s.row_cXcY]
|
|
43
43
|
S.img = [...B.imgOpacity, ...classes]
|
|
44
44
|
|
|
45
45
|
I.image = { objectFit: 'cover', aspectRatio: ratio, ...inline }
|
|
@@ -7,9 +7,18 @@ export const Scrollable = ({
|
|
|
7
7
|
scrollRef,
|
|
8
8
|
onScroll,
|
|
9
9
|
isPage,
|
|
10
|
+
x,
|
|
11
|
+
y,
|
|
12
|
+
xy,
|
|
10
13
|
}) => {
|
|
11
14
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
12
15
|
|
|
16
|
+
B.axis = xy || (x && y) ? 'xy' : x ? 'x' : 'y'
|
|
17
|
+
B.isXY = B.axis === 'xy' ? [s.overflowScroll] : []
|
|
18
|
+
B.isX = B.axis === 'x' ? [s.overflowXScroll, s.overflowYHidden] : []
|
|
19
|
+
B.isY = B.axis === 'y' ? [s.overflowYScroll] : []
|
|
20
|
+
B.scroll = [...B.isXY, ...B.isX, ...B.isY]
|
|
21
|
+
|
|
13
22
|
const pageInline = isPage
|
|
14
23
|
? { paddingBottom: 'env(safe-area-inset-bottom, 0px)' }
|
|
15
24
|
: {}
|
|
@@ -22,7 +31,7 @@ export const Scrollable = ({
|
|
|
22
31
|
return (
|
|
23
32
|
<div
|
|
24
33
|
ref={scrollRef}
|
|
25
|
-
className={[...classes,
|
|
34
|
+
className={[...classes, ...B.scroll].join(' ')}
|
|
26
35
|
style={{ ...scrollInline, ...inline, ...pageInline }}
|
|
27
36
|
onScroll={onScroll}
|
|
28
37
|
>
|
|
@@ -37,9 +37,14 @@ export const Text = (props = {}) => {
|
|
|
37
37
|
|
|
38
38
|
F.font = useGetFont(props.font, props.language)
|
|
39
39
|
B.select = props.isNoSelect ? [s.noSelect] : []
|
|
40
|
+
B.mode = props.ellipsis || props.oneLine
|
|
41
|
+
? [s.textline]
|
|
42
|
+
: props.wrap || props.preserveWhitespace
|
|
43
|
+
? [s.textwrap]
|
|
44
|
+
: [s.textblock]
|
|
40
45
|
|
|
41
46
|
const next = { ...props }
|
|
42
|
-
next.classes = [...classes, ...B.select,
|
|
47
|
+
next.classes = [...classes, ...B.select, ...B.mode, F.font]
|
|
43
48
|
|
|
44
49
|
if (props.tag) return <TextTag {...next} />
|
|
45
50
|
else return <TextSpan {...next} />
|
|
@@ -62,6 +67,10 @@ export const Textline = (props = {}) => {
|
|
|
62
67
|
else return <TextSpan {...next} />
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
export const TextEllipsis = (props = {}) => {
|
|
71
|
+
return <Textline {...props} ellipsis />
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
// use it for inline code where u need to pass the style to a span
|
|
66
75
|
export const TextFont = (props = {}) => {
|
|
67
76
|
|
|
@@ -85,7 +94,7 @@ export const TextCodeView = ({
|
|
|
85
94
|
F.font = useGetFont(f.m1)
|
|
86
95
|
|
|
87
96
|
B.isInline = type === 'inline' ? [s.textblock] : []
|
|
88
|
-
B.isBlock = type === 'block' ? [s.textwrap, s.
|
|
97
|
+
B.isBlock = type === 'block' ? [s.textwrap, s.overflowYScroll] : []
|
|
89
98
|
B.isView = type === 'view' ? [s.textwrap] : []
|
|
90
99
|
|
|
91
100
|
S.classes = [...classes, F.font, ...B.isInline, ...B.isBlock, ...B.isView].join(' ')
|
|
@@ -20,8 +20,8 @@ class Service {
|
|
|
20
20
|
const response = await store.match(key)
|
|
21
21
|
if (!response) return true
|
|
22
22
|
const lastDay = (await response.text()).trim()
|
|
23
|
-
return
|
|
23
|
+
return lastDay < this.todayUTC()
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const service = new Service()
|
|
27
|
+
export const service = new Service()
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
<meta http-equiv="Expires" content="0">
|
|
13
13
|
|
|
14
14
|
<meta content="#ffffff" name="msapplication-TileColor" />
|
|
15
|
-
<meta content="/icons/icon-512.png" name="msapplication-TileImage" />
|
|
15
|
+
<meta content="/get/media/icons/icon-512.png" name="msapplication-TileImage" />
|
|
16
16
|
<meta content="Carlo" name="apple-mobile-web-app-title" />
|
|
17
|
-
<link href="/icons/icon-180.png" rel="apple-touch-icon" sizes="180x180" />
|
|
17
|
+
<link href="/get/media/icons/icon-180.png" rel="apple-touch-icon" sizes="180x180" />
|
|
18
18
|
<link href="/index.webmanifest" rel="manifest" />
|
|
19
19
|
|
|
20
|
-
<link href="/favicon.ico" rel="shortcut icon" />
|
|
21
|
-
<link href="/icons/favicon.png" rel="icon" type="image/png" />
|
|
20
|
+
<link href="/get/media/icons/favicon.ico" rel="shortcut icon" />
|
|
21
|
+
<link href="/get/media/icons/favicon.png" rel="icon" type="image/png" />
|
|
22
22
|
|
|
23
23
|
<!-- {{metadata}} -->
|
|
24
24
|
<!-- {{languages}} -->
|
|
@@ -33,4 +33,4 @@
|
|
|
33
33
|
<script type="module" src="/App.js"></script>
|
|
34
34
|
</body>
|
|
35
35
|
|
|
36
|
-
</html>
|
|
36
|
+
</html>
|
|
@@ -6,8 +6,8 @@ export const ComingSoonPod = ({ picture }) => {
|
|
|
6
6
|
|
|
7
7
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
8
8
|
|
|
9
|
-
S.box = [s.col_cXsY, s.
|
|
10
|
-
S.image = [s.
|
|
9
|
+
S.box = [s.col_cXsY, s.XY100p, s.marginB8]
|
|
10
|
+
S.image = [s.X100p, s.radiusB16]
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<View classes={S.box}>
|
|
@@ -14,7 +14,7 @@ export const SelectItemPod = ({ onPress=()=>{}, slug, value, img, isHover, setIs
|
|
|
14
14
|
|
|
15
15
|
S.item = [s.row_sXcY, s.Y48, s.radius8, s.marginB4, ...B.background, ...B.img]
|
|
16
16
|
S.imgBox = [s.X26, s.Y20, s.marginX7, s.marginY18, s.radius5, s.border1, c.line_NN, c.item_NN_B400]
|
|
17
|
-
S.img = [s.X24, s.Y18, s.
|
|
17
|
+
S.img = [s.X24, s.Y18, s.overflowHidden, s.radius4]
|
|
18
18
|
S.text = [...F.text, s.row_sXcY, s.paddingE12, c.item_NN_B400]
|
|
19
19
|
|
|
20
20
|
return (
|
|
@@ -17,7 +17,7 @@ export const SelectPickedPod = ({ value, slug, img, isHover, setIsHover=()=>{},
|
|
|
17
17
|
|
|
18
18
|
S.item = [s.row_sXcY, s.Y40, s.radius8, s.Xmin80, s.border1, c.line_NN_W500, ...B.hover, ...B.img]
|
|
19
19
|
S.imgBox = [s.X26, s.Y20, s.marginX7, s.radius5, s.border1, c.line_NN, c.item_NN_B400]
|
|
20
|
-
S.img = [s.X24, s.Y18, s.
|
|
20
|
+
S.img = [s.X24, s.Y18, s.overflowHidden, s.radius4]
|
|
21
21
|
S.textBox = [s.row_cY]
|
|
22
22
|
S.text = [...F.text, s.Xr, c.item_NN_B400]
|
|
23
23
|
S.icon = [s.XY40, B.icon]
|
|
@@ -11,8 +11,8 @@ export const SlideshowPod = ({ svg, onNext, onPrevious, aspectRatio, children })
|
|
|
11
11
|
|
|
12
12
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
13
13
|
|
|
14
|
-
S.slider = [s.row_cXcY, s.
|
|
15
|
-
S.controls = [s.row, s.
|
|
14
|
+
S.slider = [s.row_cXcY, s.X100p, s.positionRelative]
|
|
15
|
+
S.controls = [s.row, s.XY100p, s.Z3]
|
|
16
16
|
S.one = [s.row_sXcY, s.XYr]
|
|
17
17
|
S.iconOneBox = [s.XY64, s.col_cXcY, s.Z2]
|
|
18
18
|
S.iconOne = [s.XY40, s.Z2, s.marginX8, c.item_NN_B400, s.radius8, c.base_WW_50]
|
|
@@ -46,7 +46,7 @@ export const SlideshowTextualPod = ({ subhead, title, description, href, fontSiz
|
|
|
46
46
|
|
|
47
47
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
48
48
|
|
|
49
|
-
S.box = [s.row_sXcY, s.
|
|
49
|
+
S.box = [s.row_sXcY, s.positionAbsolute, s.XY100p, s.top0]
|
|
50
50
|
S.textual = [s.col_sXsY]
|
|
51
51
|
S.outline = [s.paddingX4, s.paddingY2, s.margin4, c.item_WW]
|
|
52
52
|
|
|
@@ -76,7 +76,7 @@ export const SlideshowItemPod = ({ isActive, picture, children, aspectRatio, tra
|
|
|
76
76
|
|
|
77
77
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
78
78
|
|
|
79
|
-
S.image = [s.
|
|
79
|
+
S.image = [s.X100p]
|
|
80
80
|
I.image = { aspectRatio }
|
|
81
81
|
|
|
82
82
|
return (
|
|
@@ -5,18 +5,18 @@ export const getCards = (s, c, t) => {
|
|
|
5
5
|
|
|
6
6
|
cards.squared = {}
|
|
7
7
|
cards.squared.link = []
|
|
8
|
-
cards.squared.box = [s.
|
|
9
|
-
cards.squared.pictureWrap = [s.
|
|
10
|
-
cards.squared.pictureBox = [s.
|
|
11
|
-
cards.squared.picture = [s.
|
|
8
|
+
cards.squared.box = [s.X100p, s.radius24, s.overflowHidden, c.base_WW]
|
|
9
|
+
cards.squared.pictureWrap = [s.X100p, s.padding8]
|
|
10
|
+
cards.squared.pictureBox = [s.X100p, s.ratio, s.overflowHidden, s.radius16]
|
|
11
|
+
cards.squared.picture = [s.objectCover]
|
|
12
12
|
cards.squared.content = [s.paddingX16, s.paddingY16, s.col]
|
|
13
13
|
|
|
14
14
|
cards.portrait = {}
|
|
15
|
-
cards.portrait.link = [s.
|
|
16
|
-
cards.portrait.box = [s.
|
|
17
|
-
cards.portrait.pictureWrap = [s.
|
|
18
|
-
cards.portrait.pictureBox = [s.
|
|
19
|
-
cards.portrait.picture = [s.
|
|
15
|
+
cards.portrait.link = [s.Y100p]
|
|
16
|
+
cards.portrait.box = [s.X100p, s.Y100p, s.col, s.padding24, s.radius24, s.overflowHidden, s.border1, c.base_WW, c.line_NN_W600]
|
|
17
|
+
cards.portrait.pictureWrap = [s.X100p]
|
|
18
|
+
cards.portrait.pictureBox = [s.X100p, s.overflowHidden, s.radius16]
|
|
19
|
+
cards.portrait.picture = [s.X100p]
|
|
20
20
|
cards.portrait.content = [s.paddingT16, s.col, s.Yc]
|
|
21
21
|
cards.portrait.title = []
|
|
22
22
|
cards.portrait.price = [s.marginT16]
|
|
@@ -6,10 +6,10 @@ export const getPage = (s, c, t) => {
|
|
|
6
6
|
|
|
7
7
|
page.home = [s.col, s.XYc]
|
|
8
8
|
page.fluid = [s.col, s.XYc]
|
|
9
|
-
page.fixed = [s.col_cXsY, s.Yc, s.
|
|
10
|
-
page.fixedL = [s.col_cXsY, s.Yc, s.
|
|
11
|
-
page.fixedXL = [s.col_cXsY, s.Yc, s.
|
|
12
|
-
page.docs = [s.col_cXsY, s.Yc, s.
|
|
9
|
+
page.fixed = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax540, s.paddingX16]
|
|
10
|
+
page.fixedL = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax672, s.paddingX16]
|
|
11
|
+
page.fixedXL = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax960, s.paddingX16]
|
|
12
|
+
page.docs = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax540, s.paddingX16, s.marginT16]
|
|
13
13
|
page.card = [s.col_cX, s.Yc, s.paddingX16]
|
|
14
14
|
|
|
15
15
|
return page
|
|
@@ -21,7 +21,7 @@ export const getRoot = (s, c, t) => {
|
|
|
21
21
|
root.cards.text.title = [s.textS, s.fs16, s.lh24, s.fw700]
|
|
22
22
|
root.cards.text.description = [s.textS, s.fs16, s.lh20, s.fw400]
|
|
23
23
|
root.cards.text.label = [s.textS, s.fs16, s.lh24, s.fw600]
|
|
24
|
-
root.cards.box = [s.col, s.
|
|
24
|
+
root.cards.box = [s.col, s.X100p]
|
|
25
25
|
root.cards.top = [s.col, s.paddingT16, s.paddingB8]
|
|
26
26
|
root.cards.title = [...root.cards.text.title, c.item_NN_B400]
|
|
27
27
|
root.cards.description = [...root.cards.text.description, c.item_NN_B400]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class Pulse {
|
|
2
|
+
|
|
3
|
+
get = async () => this.formatDate(new Date())
|
|
4
|
+
|
|
5
|
+
formatDate = date => {
|
|
6
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
7
|
+
weekday: 'short',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
month: 'short',
|
|
10
|
+
year: 'numeric',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
hour12: false,
|
|
14
|
+
timeZone: 'UTC'
|
|
15
|
+
}).formatToParts(date)
|
|
16
|
+
|
|
17
|
+
const value = type => parts.find(part => part.type === type)?.value ?? ''
|
|
18
|
+
return `${value('weekday')} ${value('day')} ${value('month')} ${value('year')} ${value('hour')}:${value('minute')}`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Pulse }
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { Router } from '#@/index.dep.js'
|
|
2
2
|
import { middleware } from '#@/server/middleware.js'
|
|
3
3
|
import { resolver } from '#@/server/resolvers.js'
|
|
4
|
+
import { Pulse } from '#@/modules/pulse.js'
|
|
4
5
|
import { sync } from '#@/modules/sync.server.js'
|
|
5
6
|
import { clone } from '#@/modules/clone.js'
|
|
6
7
|
|
|
7
8
|
const rs = {}
|
|
8
9
|
|
|
10
|
+
// pulse
|
|
11
|
+
|
|
12
|
+
rs.pulse = {}
|
|
13
|
+
rs.pulse.method = 'get'
|
|
14
|
+
rs.pulse.name = 'pulse'
|
|
15
|
+
rs.pulse.path = '/pulse'
|
|
16
|
+
rs.pulse.resolve = async (...args) => await resolver.resolve(new Pulse().get, rs.pulse.path, ...args)
|
|
17
|
+
rs.pulse.funcs = [ rs.pulse.resolve ]
|
|
18
|
+
|
|
9
19
|
rs.sync_current = {}
|
|
10
20
|
rs.sync_current.method = 'post'
|
|
11
21
|
rs.sync_current.name = 'sync_current'
|
|
@@ -55,4 +65,4 @@ rs.clone.funcs = [rs.clone.auth, rs.clone.resolve]
|
|
|
55
65
|
|
|
56
66
|
const router = Router()
|
|
57
67
|
for ( const r of Object.values(rs)) router[r.method](r.path, ...r.funcs)
|
|
58
|
-
export { router, rs as routes }
|
|
68
|
+
export { router, rs as routes }
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"input": "media/trademarks",
|
|
16
16
|
"output": "get/media/trademarks"
|
|
17
17
|
},
|
|
18
|
+
"media/graphic": {
|
|
19
|
+
"worker": "media_graphic",
|
|
20
|
+
"input": "media/graphic",
|
|
21
|
+
"output": "get/media/graphic",
|
|
22
|
+
"file": "get/data/root/statics/@graphic.json"
|
|
23
|
+
},
|
|
18
24
|
"media/images": {
|
|
19
25
|
"worker": "media_images",
|
|
20
26
|
"input": "media/images",
|
|
@@ -25,6 +31,11 @@
|
|
|
25
31
|
"input": "media/covers",
|
|
26
32
|
"output": "get/media/covers"
|
|
27
33
|
},
|
|
34
|
+
"docs/pdf": {
|
|
35
|
+
"worker": "docs_pdf",
|
|
36
|
+
"input": "docs/pdf",
|
|
37
|
+
"output": "get/docs/pdf"
|
|
38
|
+
},
|
|
28
39
|
"data/root": {
|
|
29
40
|
"worker": "data_statics",
|
|
30
41
|
"input": "data/root",
|
|
@@ -51,7 +62,8 @@
|
|
|
51
62
|
"isPublisher": true,
|
|
52
63
|
"worker": "tabulars",
|
|
53
64
|
"input":"tabulars_data_content",
|
|
54
|
-
"output": "get/data/content"
|
|
65
|
+
"output": "get/data/content",
|
|
66
|
+
"outputModules": "get/data/modules"
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const help = `
|
|
2
|
+
@fullstackunicorn/create-root
|
|
3
|
+
|
|
4
|
+
Create a new FullstackUnicorn root scaffold.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
yarn create @fullstackunicorn/root <foldername>
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
yarn create @fullstackunicorn/root example.project.dev
|
|
11
|
+
|
|
12
|
+
What this command does:
|
|
13
|
+
- creates <foldername> in the current working directory
|
|
14
|
+
- copies the bundled root scaffold
|
|
15
|
+
- does not initialize dependencies
|
|
16
|
+
- does not copy client data
|
|
17
|
+
- does not run root upgrade
|
|
18
|
+
|
|
19
|
+
After create-root:
|
|
20
|
+
1. copy the prepared client root.config.app.js into <foldername>/root.config.app.js
|
|
21
|
+
2. copy prepared icons into <foldername>/workspace/volume/media/icons/
|
|
22
|
+
3. copy prepared trademarks into <foldername>/workspace/volume/media/trademarks/
|
|
23
|
+
4. cd <foldername>
|
|
24
|
+
5. yarn initialize:all
|
|
25
|
+
6. yarn root upgrade
|
|
26
|
+
|
|
27
|
+
AI safety rules:
|
|
28
|
+
- always ask for the target parent path before running this command
|
|
29
|
+
- always ask for the client data folder before initialization or root upgrade
|
|
30
|
+
- the client data folder must contain root.config.app.js, icons/, and trademarks/
|
|
31
|
+
- do not run this command with --help, -help, or help as a folder name
|
|
32
|
+
- options beginning with "-" are not valid folder names
|
|
33
|
+
`
|
package/index.cmd.js
CHANGED
|
@@ -3,17 +3,34 @@
|
|
|
3
3
|
import '@fullstackunicorn/initialize'
|
|
4
4
|
F3.service = '@fullstackunicorn/create-root'
|
|
5
5
|
import { init } from '#@/index.js'
|
|
6
|
+
import { help } from '#@/index.cmd.help.js'
|
|
6
7
|
|
|
7
8
|
const [nodePath, nodeScriptPath, foldername] = process.argv
|
|
8
9
|
|
|
10
|
+
const helpArgs = new Set(['help', '-help', '--help'])
|
|
11
|
+
|
|
12
|
+
const printHelp = () => console.log(help)
|
|
13
|
+
|
|
9
14
|
try {
|
|
10
15
|
|
|
16
|
+
if (helpArgs.has(foldername)) {
|
|
17
|
+
printHelp()
|
|
18
|
+
process.exit(0)
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
if (!F3.str.isGood(foldername)) {
|
|
12
22
|
console.error('❌ Error: missing foldername')
|
|
13
23
|
console.error('✅ Suggestion:')
|
|
14
24
|
console.error('👉 yarn create @fullstackunicorn/root foldername')
|
|
15
|
-
console.error('👉
|
|
16
|
-
|
|
25
|
+
console.error('👉 yarn create @fullstackunicorn/root --help')
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (foldername.startsWith('-')) {
|
|
30
|
+
console.error(`❌ Error: invalid option "${foldername}"`)
|
|
31
|
+
console.error('✅ Suggestion:')
|
|
32
|
+
console.error('👉 yarn create @fullstackunicorn/root foldername')
|
|
33
|
+
console.error('👉 yarn create @fullstackunicorn/root --help')
|
|
17
34
|
process.exit(1)
|
|
18
35
|
}
|
|
19
36
|
|
|
@@ -23,4 +40,4 @@ try {
|
|
|
23
40
|
} catch (err) {
|
|
24
41
|
console.error('❌ Error:', err)
|
|
25
42
|
process.exit(1)
|
|
26
|
-
}
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
2
|
+
"version": "1.0.28",
|
|
3
3
|
"name": "@fullstackunicorn/create-root",
|
|
4
4
|
"author": "lucanigido (https://fullstackunicorn.dev/author/lucanigido)",
|
|
5
5
|
"description": "create-root: bootstrapper that writes config assets and installs @fullstackunicorn/root",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"readme": "package.md",
|
|
10
10
|
"files": [
|
|
11
11
|
"index.cmd.js",
|
|
12
|
+
"index.cmd.help.js",
|
|
12
13
|
"index.def.js",
|
|
13
14
|
"index.dep.js",
|
|
14
15
|
"index.js",
|