@uniweb/runtime 0.2.5 → 0.2.7

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.jsx +85 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/runtime",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
package/src/index.jsx CHANGED
@@ -19,6 +19,53 @@ import Blocks from './components/Blocks.jsx'
19
19
  // Core factory from @uniweb/core
20
20
  import { createUniweb } from '@uniweb/core'
21
21
 
22
+ /**
23
+ * Decode combined data from __DATA__ element
24
+ *
25
+ * Encoding is signaled via MIME type:
26
+ * - application/json: plain JSON (no compression)
27
+ * - application/gzip: gzip + base64 encoded
28
+ *
29
+ * @returns {Promise<{foundation: Object, content: Object}|null>}
30
+ */
31
+ async function decodeData() {
32
+ const el = document.getElementById('__DATA__')
33
+ if (!el?.textContent) return null
34
+
35
+ const raw = el.textContent
36
+
37
+ // Plain JSON (uncompressed)
38
+ if (el.type === 'application/json') {
39
+ try {
40
+ return JSON.parse(raw)
41
+ } catch {
42
+ return null
43
+ }
44
+ }
45
+
46
+ // Compressed (application/gzip or legacy application/octet-stream)
47
+ if (typeof DecompressionStream !== 'undefined') {
48
+ try {
49
+ const bytes = Uint8Array.from(atob(raw), c => c.charCodeAt(0))
50
+ const stream = new DecompressionStream('gzip')
51
+ const writer = stream.writable.getWriter()
52
+ writer.write(bytes)
53
+ writer.close()
54
+ const json = await new Response(stream.readable).text()
55
+ return JSON.parse(json)
56
+ } catch {
57
+ return null
58
+ }
59
+ }
60
+
61
+ // Fallback for old browsers: try plain JSON (server can detect User-Agent)
62
+ try {
63
+ return JSON.parse(raw)
64
+ } catch {
65
+ return null
66
+ }
67
+ }
68
+
22
69
  /**
23
70
  * Load foundation CSS from URL
24
71
  * @param {string} url - URL to foundation's CSS file
@@ -226,5 +273,42 @@ async function initRuntime(foundationSource, options = {}) {
226
273
  }
227
274
  }
228
275
 
229
- export { initRuntime }
276
+ /**
277
+ * Simplified entry point for sites
278
+ *
279
+ * Reads configuration from (in order of priority):
280
+ * 1. __DATA__ element (dynamic backends) - combined foundation config + site content
281
+ * 2. Build-time __FOUNDATION_CONFIG__ (static builds) - foundation config only
282
+ *
283
+ * @param {Object} options
284
+ * @param {Promise} options.foundation - Promise from import('#foundation')
285
+ * @param {Promise} options.styles - Promise from import('#foundation/styles')
286
+ */
287
+ async function start({ foundation, styles } = {}) {
288
+ // Try __DATA__ first (dynamic backends inject combined config + content)
289
+ const data = await decodeData()
290
+
291
+ if (data) {
292
+ // Dynamic backend mode - foundation loaded from URL, content from data
293
+ return initRuntime(
294
+ { url: data.foundation.url, cssUrl: data.foundation.cssUrl },
295
+ { configData: data.content }
296
+ )
297
+ }
298
+
299
+ // Static build mode - use build-time config
300
+ const config =
301
+ typeof __FOUNDATION_CONFIG__ !== 'undefined' ? __FOUNDATION_CONFIG__ : { mode: 'bundled' }
302
+
303
+ if (config.mode === 'runtime') {
304
+ // Runtime mode (foundation URL in site.yml)
305
+ return initRuntime({ url: config.url, cssUrl: config.cssUrl })
306
+ } else {
307
+ // Bundled mode - foundation included in build
308
+ const [foundationModule] = await Promise.all([foundation, styles])
309
+ return initRuntime(foundationModule)
310
+ }
311
+ }
312
+
313
+ export { initRuntime, start }
230
314
  export default initRuntime