@alstar/studio 0.0.0-beta.2 → 0.0.0-beta.4

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.
@@ -34,7 +34,16 @@ export default (props: {
34
34
  type="module"
35
35
  src="https://cdn.jsdelivr.net/gh/starfederation/datastar@main/bundles/datastar.js"
36
36
  ></script>
37
- <script src="https://unpkg.com/@barba/core@2.10.3/dist/barba.umd.js"></script>
37
+
38
+ <script type="importmap">
39
+ {
40
+ "imports": {
41
+ "@barba/core": "https://esm.sh/@barba/core@2.10.3/dist/barba.mjs",
42
+ "sortablejs": "https://esm.sh/sortablejs@1.15.6/modular/sortable.core.esm.js",
43
+ "ink-mde": "https://esm.sh/ink-mde@0.34.0"
44
+ }
45
+ }
46
+ </script>
38
47
  </head>
39
48
 
40
49
  <body data-barba="wrapper">
package/index.ts CHANGED
@@ -14,6 +14,8 @@ import { serveStatic } from '@hono/node-server/serve-static'
14
14
  import { createStudioTables } from './utils/create-studio-tables.ts'
15
15
  import { fileBasedRouter } from './utils/file-based-router.ts'
16
16
 
17
+ export { html, type HtmlEscapedString } from './utils/html.ts'
18
+
17
19
  export let structure: types.Structure
18
20
  export let rootdir = '/node_modules/@alstar/studio'
19
21
 
@@ -22,7 +24,7 @@ const createStudio = async (studioStructure?: types.Structure) => {
22
24
 
23
25
  structure = studioStructure || []
24
26
 
25
- loadDb('./cms.db')
27
+ loadDb('./studio.db')
26
28
 
27
29
  createStudioTables()
28
30
 
@@ -65,7 +67,5 @@ const createStudio = async (studioStructure?: types.Structure) => {
65
67
  return app
66
68
  }
67
69
 
68
- export const defineConfig = (config: types.StudioConfig) => config
69
- export const defineStructure = (structure: types.Structure) => structure
70
-
70
+ export { defineConfig, defineEntry, defineStructure } from './utils/define.ts'
71
71
  export { createStudio, query }
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@alstar/studio",
3
- "version": "0.0.0-beta.2",
3
+ "version": "0.0.0-beta.4",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "dependencies": {
7
7
  "@hono/node-server": "^1.18.1",
8
8
  "@starfederation/datastar-sdk": "1.0.0-RC.1",
9
9
  "hono": "^4.8.12",
10
- "sortablejs": "^1.15.6",
11
- "@alstar/ui": "0.0.0-beta.1",
12
- "@alstar/db": "0.0.0-beta.1"
10
+ "@alstar/db": "0.0.0-beta.1",
11
+ "@alstar/ui": "0.0.0-beta.1"
13
12
  },
14
13
  "devDependencies": {
15
14
  "@types/node": "^24.1.0",
package/public/main.css CHANGED
@@ -1,4 +1,5 @@
1
- @import './../node_modules/@alstar/ui/red.css';
1
+ /* @import './../node_modules/@alstar/ui/red.css'; */
2
+ @import 'https://esm.sh/@alstar/ui/red.css';
2
3
 
3
4
  body {
4
5
  min-height: 100vh;
package/public/main.js CHANGED
@@ -1,5 +1,6 @@
1
- import Sortable from '../node_modules/sortablejs/modular/sortable.core.esm.js'
2
- import { ink, wrap } from 'https://esm.sh/ink-mde@0.34.0'
1
+ import barba from '@barba/core'
2
+ import Sortable from 'sortablejs'
3
+ // import { wrap } from 'ink-mde'
3
4
 
4
5
  barba.init({
5
6
  views: [
@@ -27,17 +28,17 @@ function init() {
27
28
  })
28
29
  }
29
30
 
30
- // ink-mde
31
- {
32
- const el = document.querySelector('textarea')
31
+ // // ink-mde
32
+ // {
33
+ // const el = document.querySelector('textarea')
33
34
 
34
- if (el) {
35
- wrap(el, {
36
- interface: {
37
- // appearance: InkValues.Appearance.Auto,
38
- attribution: false,
39
- },
40
- })
41
- }
42
- }
35
+ // if (el) {
36
+ // wrap(el, {
37
+ // interface: {
38
+ // // appearance: InkValues.Appearance.Auto,
39
+ // attribution: false,
40
+ // },
41
+ // })
42
+ // }
43
+ // }
43
44
  }
@@ -0,0 +1,13 @@
1
+ import { type Context } from 'hono'
2
+ import * as types from '../types.ts'
3
+ import { type HttpBindings } from '@hono/node-server'
4
+ import { type BlankInput } from 'hono/types'
5
+ import { type HtmlEscapedString } from './html.ts'
6
+
7
+ export const defineConfig = (config: types.StudioConfig) => config
8
+ export const defineStructure = (structure: types.Structure) => structure
9
+ export const defineEntry = (
10
+ fn: (
11
+ c: Context<{ Bindings: HttpBindings }, string, BlankInput>,
12
+ ) => HtmlEscapedString | Promise<HtmlEscapedString>,
13
+ ) => fn
@@ -3,7 +3,7 @@ import path from 'node:path'
3
3
  import { Hono } from 'hono'
4
4
  import { type HtmlEscapedString } from 'hono/utils/html'
5
5
 
6
- type Page<C> = (c?: C) => HtmlEscapedString | Promise<HtmlEscapedString>
6
+ export type Page<C> = (c?: C) => HtmlEscapedString | Promise<HtmlEscapedString>
7
7
 
8
8
  export const fileBasedRouter = async (rootdir: string) => {
9
9
  const router = new Hono()
@@ -8,7 +8,7 @@ async function fileExists(filepath: string) {
8
8
  try {
9
9
  await fs.stat(filepath)
10
10
  } catch (error) {
11
- return {}
11
+ return null
12
12
  }
13
13
  }
14
14
 
package/utils/html.ts ADDED
@@ -0,0 +1,247 @@
1
+ export const HtmlEscapedCallbackPhase = {
2
+ Stringify: 1,
3
+ BeforeStream: 2,
4
+ Stream: 3,
5
+ } as const
6
+ type HtmlEscapedCallbackOpts = {
7
+ buffer?: [string]
8
+ phase: (typeof HtmlEscapedCallbackPhase)[keyof typeof HtmlEscapedCallbackPhase]
9
+ context: Readonly<object> // An object unique to each JSX tree. This object is used as the WeakMap key.
10
+ }
11
+ export type HtmlEscapedCallback = (
12
+ opts: HtmlEscapedCallbackOpts,
13
+ ) => Promise<string> | undefined
14
+ export type HtmlEscaped = {
15
+ isEscaped: true
16
+ callbacks?: HtmlEscapedCallback[]
17
+ }
18
+ export type HtmlEscapedString = string & HtmlEscaped
19
+
20
+ /**
21
+ * StringBuffer contains string and Promise<string> alternately
22
+ * The length of the array will be odd, the odd numbered element will be a string,
23
+ * and the even numbered element will be a Promise<string>.
24
+ * When concatenating into a single string, it must be processed from the tail.
25
+ * @example
26
+ * [
27
+ * 'framework.',
28
+ * Promise.resolve('ultra fast'),
29
+ * 'a ',
30
+ * Promise.resolve('is '),
31
+ * 'Hono',
32
+ * ]
33
+ */
34
+ export type StringBuffer = (string | Promise<string>)[]
35
+ export type StringBufferWithCallbacks = StringBuffer & {
36
+ callbacks: HtmlEscapedCallback[]
37
+ }
38
+
39
+ export const raw = (
40
+ value: unknown,
41
+ callbacks?: HtmlEscapedCallback[],
42
+ ): HtmlEscapedString => {
43
+ const escapedString = new String(value) as HtmlEscapedString
44
+ escapedString.isEscaped = true
45
+ escapedString.callbacks = callbacks
46
+
47
+ return escapedString
48
+ }
49
+
50
+ // The `escapeToBuffer` implementation is based on code from the MIT licensed `react-dom` package.
51
+ // https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/server/escapeTextForBrowser.js
52
+
53
+ const escapeRe = /[&<>'"]/
54
+
55
+ export const stringBufferToString = async (
56
+ buffer: StringBuffer,
57
+ callbacks: HtmlEscapedCallback[] | undefined,
58
+ ): Promise<HtmlEscapedString> => {
59
+ let str = ''
60
+ callbacks ||= []
61
+ const resolvedBuffer = await Promise.all(buffer)
62
+ for (let i = resolvedBuffer.length - 1; ; i--) {
63
+ str += resolvedBuffer[i]
64
+ i--
65
+ if (i < 0) {
66
+ break
67
+ }
68
+
69
+ let r = resolvedBuffer[i]
70
+ if (typeof r === 'object') {
71
+ callbacks.push(...((r as HtmlEscapedString).callbacks || []))
72
+ }
73
+
74
+ const isEscaped = (r as HtmlEscapedString).isEscaped
75
+ r = await (typeof r === 'object' ? (r as HtmlEscapedString).toString() : r)
76
+ if (typeof r === 'object') {
77
+ callbacks.push(...((r as HtmlEscapedString).callbacks || []))
78
+ }
79
+
80
+ if ((r as HtmlEscapedString).isEscaped ?? isEscaped) {
81
+ str += r
82
+ } else {
83
+ const buf = [str]
84
+ escapeToBuffer(r, buf)
85
+ str = buf[0]
86
+ }
87
+ }
88
+
89
+ return raw(str, callbacks)
90
+ }
91
+
92
+ export const escapeToBuffer = (str: string, buffer: StringBuffer): void => {
93
+ const match = str.search(escapeRe)
94
+ if (match === -1) {
95
+ buffer[0] += str
96
+ return
97
+ }
98
+
99
+ let escape
100
+ let index
101
+ let lastIndex = 0
102
+
103
+ for (index = match; index < str.length; index++) {
104
+ switch (str.charCodeAt(index)) {
105
+ case 34: // "
106
+ escape = '&quot;'
107
+ break
108
+ case 39: // '
109
+ escape = '&#39;'
110
+ break
111
+ case 38: // &
112
+ escape = '&amp;'
113
+ break
114
+ case 60: // <
115
+ escape = '&lt;'
116
+ break
117
+ case 62: // >
118
+ escape = '&gt;'
119
+ break
120
+ default:
121
+ continue
122
+ }
123
+
124
+ buffer[0] += str.substring(lastIndex, index) + escape
125
+ lastIndex = index + 1
126
+ }
127
+
128
+ buffer[0] += str.substring(lastIndex, index)
129
+ }
130
+
131
+ export const resolveCallbackSync = (
132
+ str: string | HtmlEscapedString,
133
+ ): string => {
134
+ const callbacks = (str as HtmlEscapedString)
135
+ .callbacks as HtmlEscapedCallback[]
136
+ if (!callbacks?.length) {
137
+ return str
138
+ }
139
+ const buffer: [string] = [str]
140
+ const context = {}
141
+
142
+ callbacks.forEach((c) =>
143
+ c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }),
144
+ )
145
+
146
+ return buffer[0]
147
+ }
148
+
149
+ export const resolveCallback = async (
150
+ str: string | HtmlEscapedString | Promise<string>,
151
+ phase: (typeof HtmlEscapedCallbackPhase)[keyof typeof HtmlEscapedCallbackPhase],
152
+ preserveCallbacks: boolean,
153
+ context: object,
154
+ buffer?: [string],
155
+ ): Promise<string> => {
156
+ if (typeof str === 'object' && !(str instanceof String)) {
157
+ if (!((str as unknown) instanceof Promise)) {
158
+ str = (str as unknown as string).toString() // HtmlEscapedString object to string
159
+ }
160
+ if ((str as string | Promise<string>) instanceof Promise) {
161
+ str = await (str as unknown as Promise<string>)
162
+ }
163
+ }
164
+
165
+ const callbacks = (str as HtmlEscapedString)
166
+ .callbacks as HtmlEscapedCallback[]
167
+ if (!callbacks?.length) {
168
+ return Promise.resolve(str)
169
+ }
170
+ if (buffer) {
171
+ buffer[0] += str
172
+ } else {
173
+ buffer = [str as string]
174
+ }
175
+
176
+ const resStr = Promise.all(
177
+ callbacks.map((c) => c({ phase, buffer, context })),
178
+ ).then((res) =>
179
+ Promise.all(
180
+ res
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ .filter<string>(Boolean as any)
183
+ .map((str) => resolveCallback(str, phase, false, context, buffer)),
184
+ ).then(() => (buffer as [string])[0]),
185
+ )
186
+
187
+ if (preserveCallbacks) {
188
+ return raw(await resStr, callbacks)
189
+ } else {
190
+ return resStr
191
+ }
192
+ }
193
+
194
+ export const html = (
195
+ strings: TemplateStringsArray,
196
+ ...values: unknown[]
197
+ ): HtmlEscapedString | Promise<HtmlEscapedString> => {
198
+ const buffer: StringBufferWithCallbacks = [''] as StringBufferWithCallbacks
199
+
200
+ for (let i = 0, len = strings.length - 1; i < len; i++) {
201
+ buffer[0] += strings[i]
202
+
203
+ const children = Array.isArray(values[i])
204
+ ? (values[i] as Array<unknown>).flat(Infinity)
205
+ : [values[i]]
206
+ for (let i = 0, len = children.length; i < len; i++) {
207
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
+ const child = children[i] as any
209
+ if (typeof child === 'string') {
210
+ escapeToBuffer(child, buffer)
211
+ } else if (typeof child === 'number') {
212
+ ;(buffer[0] as string) += child
213
+ } else if (
214
+ typeof child === 'boolean' ||
215
+ child === null ||
216
+ child === undefined
217
+ ) {
218
+ continue
219
+ } else if (
220
+ typeof child === 'object' &&
221
+ (child as HtmlEscaped).isEscaped
222
+ ) {
223
+ if ((child as HtmlEscapedString).callbacks) {
224
+ buffer.unshift('', child)
225
+ } else {
226
+ const tmp = child.toString()
227
+ if (tmp instanceof Promise) {
228
+ buffer.unshift('', tmp)
229
+ } else {
230
+ buffer[0] += tmp
231
+ }
232
+ }
233
+ } else if (child instanceof Promise) {
234
+ buffer.unshift('', child)
235
+ } else {
236
+ escapeToBuffer(child.toString(), buffer)
237
+ }
238
+ }
239
+ }
240
+ buffer[0] += strings.at(-1) as string
241
+
242
+ return buffer.length === 1
243
+ ? 'callbacks' in buffer
244
+ ? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks)))
245
+ : raw(buffer[0])
246
+ : stringBufferToString(buffer, buffer.callbacks)
247
+ }