kireji 0.1.1 → 0.3.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kireji",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "A web framework for stateful, entropy-perfect, multi-origin web applications. Currently in alpha. Expect breaking changes for version 0. Use with caution!",
5
5
  "files": [
6
6
  "src/",
Binary file
@@ -3,10 +3,7 @@ const tabBitDepths = [0n, 0n]
3
3
  const tabOffsets = [0n]
4
4
  const permutationSizes = [1n]
5
5
  const payloadSizes = [1n]
6
- const partOffsets = [0, allParts.length]
7
- const subjectCount = BigInt(allParts.reduce((subjectCount, part, i) =>
8
- partOffsets[i + 2] = subjectCount + part.filenames.length, allParts.length
9
- ))
6
+ const subjectCount = BigInt(allSubjects.length)
10
7
  const maxTabCount = subjectCount
11
8
  const LSB = []
12
9
  const powerFloor = 2n ** BigInt(subjectCount.toString(2).length - 1)
@@ -63,8 +60,6 @@ tabGroup.define({
63
60
  tabBitDepths: { value: tabBitDepths },
64
61
  permutationSizes: { value: permutationSizes },
65
62
  payloadSizes: { value: payloadSizes },
66
- partOffsets: { value: partOffsets },
67
- subjectCount: { value: subjectCount },
68
63
  maxTabCount: { value: maxTabCount },
69
64
  previousPart: { value: null, writable: true },
70
65
  tree: { value: null, writable: true },
@@ -1,4 +1,4 @@
1
- const indexOfLastPossibleTabSubject = tabGroup.subjectCount - 1n
1
+ const indexOfLastPossibleTabSubject = BigInt(allSubjects.length) - 1n
2
2
  const indexOfLastOpenTab = NUMBER_OF_TABS_OPEN - 1n
3
3
 
4
4
  let permutationFactorOfCurrentTabIndex = 1n
@@ -46,16 +46,7 @@
46
46
  display: none;
47
47
  }
48
48
 
49
- #kireji_app tab-[data-drag-preview] {
50
- position: fixed;
51
- pointer-events: none;
52
- }
53
-
54
- #kireji_app tab-[data-drag-preview] .close-tab {
55
- display: none;
56
- }
57
-
58
- #kireji_app tab- {
49
+ tab- {
59
50
  height: fit-content;
60
51
  white-space: pre;
61
52
  text-overflow: ellipsis;
@@ -71,12 +62,12 @@
71
62
  line-height: var(--tab-line-height);
72
63
  }
73
64
 
74
- #kireji_app tab- img {
65
+ tab- img {
75
66
  height: var(--tab-icon-size);
76
67
  margin: calc((var(--tab-group-height) - var(--tab-icon-size)) / 2) 0;
77
68
  }
78
69
 
79
- #kireji_app tab- .tab-button {
70
+ tab- .tab-button {
80
71
  font-size: var(--default-font-size);
81
72
  font-weight: 400;
82
73
  height: unset;
@@ -105,6 +96,20 @@
105
96
  z-index: 999;
106
97
  }
107
98
 
99
+ tab-[data-drag-preview] {
100
+ width: min-content;
101
+ pointer-events: none;
102
+ position: fixed;
103
+ }
104
+
105
+ tab-[data-drag-preview] .tab-button {
106
+ padding-right: calc(var(--spacing) / 1.5);
107
+ }
108
+
109
+ tab-[data-drag-preview] .close-tab {
110
+ display: none;
111
+ }
112
+
108
113
  #kireji_app tab-[data-drop-target="after"]::before {
109
114
  right: -1px;
110
115
  }
@@ -156,7 +161,7 @@ body.modern.light #kireji_app tab-:is([data-active], :not([data-active]):hover)
156
161
  background-color: var(--bg-light-est);
157
162
  }
158
163
 
159
- body.modern #kireji_app tab- {
164
+ body.modern tab- {
160
165
  box-shadow:
161
166
  inset 0 -2px 0 -1px var(--bg-un-mode),
162
167
  inset 0 2px 0 -1px var(--bg-un-mode),
@@ -169,7 +174,12 @@ body.modern #kireji_app tab-[data-active] {
169
174
  inset -2px 0 0 -1px var(--bg-un-mode);
170
175
  }
171
176
 
172
- body.modern #kireji_app tab- .tab-button {
177
+ body.modern>tab-[data-drag-preview] {
178
+ box-shadow: inset 0 0 0 1px var(--bg-un-mode);
179
+ background-color: var(--bg-light-est);
180
+ }
181
+
182
+ body.modern tab- .tab-button {
173
183
  line-height: var(--tab-group-height);
174
184
  }
175
185
 
@@ -179,10 +189,6 @@ body.modern #kireji_app tab- .tab-path {
179
189
  display: inline-block;
180
190
  }
181
191
 
182
- body.modern #kireji_app tab-[data-drag-preview] .tab-path {
183
- display: none;
184
- }
185
-
186
192
  body.vintage #kireji_app #tab-group {
187
193
  height: calc(4px + var(--tab-group-height));
188
194
  background: transparent;
@@ -222,11 +228,11 @@ body.vintage #kireji_app tab-[data-active] {
222
228
  z-index: 2;
223
229
  }
224
230
 
225
- body.vintage #kireji_app tab- .tab-path {
231
+ /*body.vintage #kireji_app tab- .tab-path {
226
232
  display: none;
227
- }
233
+ }*/
228
234
 
229
- body.vintage #kireji_app tab-:not([data-active]) {
235
+ body.vintage tab-:not([data-active]) {
230
236
  height: var(--tab-group-height);
231
237
  position: relative;
232
238
  top: 2px;
@@ -239,7 +245,7 @@ body.vintage #kireji_app tab-[data-active] .tab-button {
239
245
  padding-left: calc(var(--spacing) / 1.5 + 2px);
240
246
  }
241
247
 
242
- body.vintage #kireji_app tab- .tab-button {
248
+ body.vintage tab- .tab-button {
243
249
  padding-right: calc(var(--tab-group-height) + 8px);
244
250
  border-top-left-radius: 2px;
245
251
  border-top-right-radius: 2px;
@@ -251,7 +257,7 @@ body.vintage #kireji_app tab- .tab-button {
251
257
  inset -3px 0 0 -1px var(--bg-dark);
252
258
  }
253
259
 
254
- body.dark.vintage #kireji_app tab- .tab-button {
260
+ body.dark.vintage tab- .tab-button {
255
261
  box-shadow:
256
262
  inset -2px 0 0 -1px black,
257
263
  inset 1px 1px 0 0 var(--bg-light-er),
@@ -279,7 +285,7 @@ body.vintage #kireji_app tab-[data-active] .tab-button:focus::after {
279
285
  --inset: 4px;
280
286
  }
281
287
 
282
- body.vintage #kireji_app tab-[data-drag-preview] .tab-button {
288
+ body.vintage>tab-[data-drag-preview] .tab-button {
283
289
  box-shadow: inset 0 0 0 1px black;
284
290
  }
285
291
 
@@ -37,7 +37,7 @@ if (numberOfTabsOpen !== tabGroup.openTabs.length || tabGroup.permutationRouteID
37
37
  tabGroup.tree = new tabGroup.FenwickTree()
38
38
 
39
39
  const indexOfLastOpenTab = numberOfTabsOpen - 1n
40
- const indexOfLastPossibleTabSubject = tabGroup.subjectCount - 1n
40
+ const indexOfLastPossibleTabSubject = BigInt(allSubjects.length) - 1n
41
41
 
42
42
  for (let currentTabIndex = 0n; currentTabIndex < numberOfTabsOpen; currentTabIndex++) {
43
43
 
@@ -56,29 +56,12 @@ if (numberOfTabsOpen !== tabGroup.openTabs.length || tabGroup.permutationRouteID
56
56
  const payload = payloadRouteID % tabGroup.payloadCardinality
57
57
  payloadRouteID /= tabGroup.payloadCardinality
58
58
 
59
- // Using embedded match logic, split apart the true index into usable file data.
60
- if (trueIndexOfActiveTabSubject < allParts.length) {
61
-
62
- // The index is in the first plane; it maps to the set of parts themselves.
63
- tabGroup.openTabs[currentTabIndex] = {
64
- part: allParts[trueIndexOfActiveTabSubject],
65
- payload
66
- }
67
- } else {
68
-
69
- // The index is among the later planes, indicating a specific file defined on a specific part.
70
- for (let indexOfPlane = 1; indexOfPlane <= allParts.length; indexOfPlane++) {
71
- const nextPlaneIndex = indexOfPlane + 1
72
- if (nextPlaneIndex > allParts.length || trueIndexOfActiveTabSubject < tabGroup.partOffsets[nextPlaneIndex]) {
73
- const indexOfOwningPart = indexOfPlane - 1
74
- const part = allParts[indexOfOwningPart]
75
- const firstIndexOfCurrentPlane = tabGroup.partOffsets[indexOfPlane]
76
- const indexOfSubjectWithinCurrentPlane = Number(trueIndexOfActiveTabSubject) - firstIndexOfCurrentPlane
77
- const filename = part.filenames[indexOfSubjectWithinCurrentPlane]
78
- tabGroup.openTabs[currentTabIndex] = { part, filename, payload }
79
- break
80
- }
81
- }
59
+ // Match the subject index with the actual subject.
60
+ const [host, filename] = allSubjects[Number(trueIndexOfActiveTabSubject)]
61
+ tabGroup.openTabs[currentTabIndex] = {
62
+ part: partsByHost[host],
63
+ filename,
64
+ payload
82
65
  }
83
66
  }
84
67
 
@@ -7,10 +7,9 @@ tabGroup.tree = new tabGroup.FenwickTree()
7
7
  let permutationRouteID = 0n
8
8
  for (let currentTabIndex = 0n; currentTabIndex < numberOfTabsOpen; currentTabIndex++) {
9
9
 
10
- // Use an embedded match to combine the current tab's file data into an absolute tab subject index.
10
+ // Get the absolute tab subject index.
11
11
  const { part, filename } = TABS[Number(currentTabIndex)]
12
- const index = allParts.indexOf(part)
13
- const trueIndexOfActiveTabSubject = BigInt(filename ? tabGroup.partOffsets[index + 1] + part.filenames.indexOf(filename) : index)
12
+ const trueIndexOfActiveTabSubject = BigInt(subjectIndices.get(part.host + (filename ? "/" + filename : "")))
14
13
 
15
14
  // Use the Fenwick tree to obtain the availability-based index of the tab subject in the list of remaining subjects.
16
15
  const availabilityIndexOfActiveTabSubject = tabGroup.tree.query(trueIndexOfActiveTabSubject - 1n)
@@ -57,10 +57,6 @@ declare interface IKirejiAppTabGroup
57
57
  readonly activeTabIndex: number
58
58
  /** The index of the preview tab, if one exists. The preview tab is the tab which can be replaced when opening a new tab. */
59
59
  readonly previewTabIndex?: number
60
- /** The array of memoized offset route IDs corresponding to the start of each part's filename plane. */
61
- readonly partOffsets: number[]
62
- /** The total number of tab subjects available - the number of _different_ filenames and part summaries that can be the focus of their own tab (regardless of the maximum number of tabs which can be open at once). */
63
- readonly subjectCount: bigint
64
60
  /** A data type which can be used to performantly rank and unrank permutation indices. */
65
61
  readonly FenwickTree: typeof FenwickTree
66
62
  }
Binary file
@@ -9,14 +9,14 @@
9
9
  --sidebar-width: calc(var(--tool-bar-width) + var(--sidebar-view-width));
10
10
  }
11
11
 
12
- body.modern #kireji_app {
12
+ body.modern {
13
13
  --tab-group-height: calc(var(--tab-line-height) + var(--spacing) / 1.5);
14
14
  --tab-line-height: 26px;
15
15
  --crumbs-height: 26px;
16
16
  --tab-icon-size: 20px;
17
17
  }
18
18
 
19
- body.vintage #kireji_app {
19
+ body.vintage {
20
20
  --tab-group-height: calc(var(--tab-line-height) + 2px);
21
21
  --tab-line-height: 20px;
22
22
  --crumbs-height: 20px;
package/src/build.js CHANGED
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
- function ƒ(_) {
2
+ function ƒ(_, compressedSubjectOrigins) {
3
3
 
4
4
  // Initialization
5
5
  const
6
6
  environment = globalThis.constructor === globalThis.Window ? "client" : globalThis.constructor === globalThis.ServiceWorkerGlobalScope ? "worker" : (
7
7
  Object.defineProperty(_, "$", { value: (f => x => f(x).toString().trim())(require("child_process").execSync) }),
8
8
  _.command = process.argv[2] || "help",
9
- _.local = _.command === "dev",
9
+ _.local = _.command === "dev" ? "1" : "0",
10
10
  require.main === module && (
11
11
  _.branch = _.$("git rev-parse --abbrev-ref HEAD").toString().trim(),
12
12
  _.gitSHA = _.$("git rev-parse HEAD").toString().trim(),
13
- _.version = (([M, m, p], c) => _.local ? +M && c === "major" ? `${++M}.0.0` : c === "minor" || (!+M && c === "major") ? `${M}.${++m}.0` : `${M}.${m}.${++p}` : `${M}.${m}.${p}`)(_.$("git log -1 --pretty=%s").toString().match(/^\s*(\d+\.\d+\.\d+)/)[1].split("."), _.change),
13
+ _.version = (([M, m, p], c) => +_.local ? +M && c === "major" ? `${++M}.0.0` : c === "minor" || (!+M && c === "major") ? `${M}.${++m}.0` : `${M}.${m}.${++p}` : `${M}.${m}.${p}`)(_.$("git log -1 --pretty=%s").toString().match(/^\s*(\d+\.\d+\.\d+)/)[1].split("."), _.change),
14
14
  _.modified = _.$('git show -s --format=%ci HEAD').toString().trim(),
15
- _.ETag = `"${_.version}.${_.gitSHA.slice(0, 7)}${_.local ? ("." + Math.random()).slice(2, 10) : ""}"`,
15
+ _.ETag = `"${_.version}.${_.gitSHA.slice(0, 7)}${+_.local ? ("." + Math.random()).slice(2, 10) : ""}"`,
16
16
  _.name = __dirname.split(/[\\/]/).filter(Boolean).at(-4)
17
17
  ),
18
18
  "node"
19
19
  ),
20
- production = _.branch === "main" && environment !== "node" && !_.local,
20
+ production = _.branch === "main" && environment !== "node" && !(+_.local),
21
21
  welcomeMessage = `
22
22
  ▌ ▘ ▘▘ ${_.name}
23
23
  k = ▙▘▌▛▘█▌ ▌▌ ${_.branch}
@@ -132,6 +132,9 @@ function ƒ(_) {
132
132
  pathRadix = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0",
133
133
  encodeSegment = routeID => {
134
134
 
135
+ if (typeof routeID !== "bigint")
136
+ throw new RangeError(`Segment encoder can only encode a bigint type (got ${typeof routeID})`)
137
+
135
138
  if (routeID < 0n)
136
139
  throw new RangeError("Segment encoder cannot encode a negative route ID.")
137
140
 
@@ -230,15 +233,45 @@ function ƒ(_) {
230
233
  ┊ Now booting the Kireji Web Framework ┊
231
234
  ╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╯
232
235
  `)
233
- const buildStartTime = Date.now()
236
+
237
+ const
238
+ bootStartTime = Date.now(),
239
+ allSubjects = [],
240
+ subjectIndices = new Map(),
241
+ subjectOrigins = new Map()
242
+
234
243
  if (environment === "node" && require.main === module) {
235
- const stats = { fileCount: 0, domainCount: 0 }
244
+ subjectOrigins.set("", true)
245
+
246
+ const
247
+ stats = { fileCount: 0, partCount: 0, ignoreCount: 0 },
248
+ { readdirSync: readFolder, readFileSync: readFile, existsSync: exists } = require("fs"),
249
+ { resolve } = require("path")
250
+
251
+ logScope(1, "Merging Configurations", configLog => {
252
+ for (const key in _) {
253
+ const value = _[key]
254
+ if (typeof value !== "string")
255
+ throw new TypeError(`Internal Error: found framework default config parameter that was not a string (typeof "${key}" === ${typeof value}).`)
256
+ subjectOrigins.set("/" + key, false)
257
+ }
258
+ const projectConfigPath = resolve(__dirname, "../../../src/kireji.json")
259
+ if (exists(projectConfigPath)) {
260
+ const projectConfig = require(projectConfigPath)
261
+ for (const key in projectConfig) {
262
+ const value = projectConfig[key]
263
+ if (typeof value !== "string")
264
+ throw new TypeError(`All config values in kireji.json must be strings (found typeof "${key}" === ${typeof value}).`)
265
+ _[key] = value
266
+ subjectOrigins.set("/" + key, true)
267
+ }
268
+ }
269
+ configLog("Done.")
270
+ })
271
+
236
272
  logScope(1, "Archiving Repository", archiveLog => {
237
- warn("Warning: Not yet merging with framework source files.")
238
273
  let lastLogFlush = Date.now()
239
274
  const
240
- { readdirSync: readFolder, readFileSync: readFile, existsSync: exists } = require("fs"),
241
- { resolve } = require("path"),
242
275
  batchedLogs = [],
243
276
  bufferLog = (verbosity, msg) => {
244
277
  if (verbosity > _.verbosity)
@@ -251,94 +284,69 @@ function ƒ(_) {
251
284
  lastLogFlush = now
252
285
  }
253
286
  },
254
- readRecursive = (host, baseDir, overlayDir, part, depth) => {
255
- if (host && host.length > 253)
256
- throw SyntaxError(`requested host is ${host.length} characters long, exceeding the maximum domain name length of 253. \n${host}`)
287
+ frameworkRoot = __dirname,
288
+ projectRoot = resolve(__dirname, "../../../src"),
289
+ readRecursive = domains => {
257
290
 
258
- // 1. Get entries from BOTH directories if they exist
259
- const baseEntries = exists(baseDir) ? readFolder(baseDir, { withFileTypes: true }) : []
260
- const overlayEntries = exists(overlayDir) ? readFolder(overlayDir, { withFileTypes: true }) : []
291
+ const host = [...domains].reverse().join(".")
261
292
 
262
- // 2. Create a unified map of names to entry types (Overlay wins)
263
- const unifiedEntries = new Map()
293
+ if (host.length > 253)
294
+ throw SyntaxError(`Part host would be ${host.length} characters long, exceeding the maximum host name length of 253 (${host}).`)
264
295
 
265
- // Base entries first
266
- for (const entry of baseEntries)
267
- unifiedEntries.set(entry.name, { entry, source: 'base' })
296
+ const mergedSubjects = new Map()
268
297
 
269
- // Overlays overwrite base definitions
270
- for (const entry of overlayEntries)
271
- unifiedEntries.set(entry.name, { entry, source: 'overlay' })
298
+ const frameworkPath = resolve(frameworkRoot, ...domains)
299
+ if (exists(frameworkPath))
300
+ for (const subject of readFolder(frameworkPath, { withFileTypes: true }))
301
+ mergedSubjects.set(subject.name, { subject, path: frameworkPath })
272
302
 
273
- const filenames = []
303
+ const projectPath = resolve(projectRoot, ...domains)
304
+ if (exists(projectPath))
305
+ for (const subject of readFolder(projectPath, { withFileTypes: true }))
306
+ mergedSubjects.set(subject.name, { subject, path: projectPath })
274
307
 
275
- for (const [itemName, { entry, source }] of unifiedEntries) {
276
- debug(itemName)
277
- // Ignore logic (TS files, hidden files, etc)
278
- if (itemName.startsWith(".") || itemName === "Icon" || (!host && itemName === "build.js") || itemName.endsWith(".ts")) {
279
- bufferLog(4, "".padEnd(depth, " ") + `⬚ ${itemName.padEnd(20, " ")} - ignored`)
280
- continue
281
- }
308
+ const part = domains.length ? {} : _
282
309
 
283
- // Determine paths for the next step
284
- const nextBase = `${baseDir}/${itemName}`
285
- const nextOverlay = `${overlayDir}/${itemName}`
286
-
287
- // Logic for host string construction remains...
288
- const filePath = (host ? host.split(".").reverse().join("/") + "/" : "") + itemName
289
-
290
- if (entry.isDirectory()) {
291
- bufferLog(2, "".padEnd(depth, " ") + `▼ ${itemName}/`)
292
-
293
- // RECURSION: Pass both potential paths down
294
- readRecursive(
295
- host ? (itemName ? itemName + "." + host : host) : itemName ?? "",
296
- nextBase,
297
- nextOverlay,
298
- (part[itemName] = part[itemName] || {}), // Ensure we merge into existing object if base/overlay share folder
299
- depth + 1
300
- )
301
-
302
- } else if (entry.isFile()) {
303
- // Prioritize overlay file path, fallback to base
304
- const actualPath = (source === 'overlay') ? nextOverlay : nextBase
305
- filenames.push([itemName, actualPath])
310
+ for (const [itemName, { subject, path }] of mergedSubjects)
311
+ if (itemName.startsWith(".") || itemName === "Icon" || (!host && itemName === "build.js") || itemName.endsWith(".ts")) {
312
+ stats.ignoreCount++
313
+ bufferLog(4, "".padEnd(domains.length, " ") + `⬚ ${itemName.padEnd(20, " ")} - ignored`)
314
+ } else if (subject.isDirectory()) {
315
+ stats.partCount++
316
+ bufferLog(2, "".padEnd(domains.length, " ") + `▼ ${itemName}/`)
317
+ subjectOrigins.set(itemName + (host ? "." + host : ""), path === projectPath)
318
+ part[itemName] = readRecursive([...domains, itemName])
319
+ } else if (subject.isFile()) {
320
+ stats.fileCount++
321
+ const isBinary = itemName.endsWith(".png") || itemName.endsWith(".gif")
322
+ bufferLog(3, "".padEnd(domains.length, " ") + `${isBinary ? "▣" : ""} ${itemName}`)
323
+ subjectOrigins.set(host + "/" + itemName, path === projectPath)
324
+ part[itemName] = readFile(resolve(path, itemName), isBinary ? "base64" : "utf-8")
325
+ } else {
326
+ stats.ignoreCount++
327
+ bufferLog(4, "".padEnd(domains.length, " ") + `⬚ ${itemName.padEnd(20, " ")} - ignored (exotic type)`)
306
328
  }
307
- }
308
-
309
- // File processing (remains largely the same, but uses the prioritized path)
310
- for (const [filename, filePath] of filenames) {
311
- const isBinary = filename.endsWith("png") || filename.endsWith("gif")
312
- const content = readFile(filePath, isBinary ? "base64" : "utf-8")
313
329
 
314
- part[filename] = content
315
- stats.fileCount++
316
- bufferLog(3, "".padEnd(depth, " ") + `${isBinary ? "▣" : "≡"} ${filename}`)
317
- }
318
-
319
- stats.domainCount++
330
+ return part
320
331
  }
321
332
 
322
- readRecursive("", __dirname, resolve(__dirname, "../../../src"), _, 0)
323
-
324
- // TODO: Evaluate the timing of this action.
325
- const configPath = resolve(__dirname, "../../../src/kireji.json")
326
- if (exists(configPath))
327
- Object.assign(_, require(configPath))
333
+ readRecursive([])
328
334
 
329
335
  if (batchedLogs.length)
330
336
  archiveLog(batchedLogs.join("\n") + "\n")
331
337
 
332
- archiveLog(`Archived in ${Date.now() - buildStartTime}ms`)
338
+ archiveLog(`Archived in ${Date.now() - bootStartTime}ms`)
333
339
  })
334
340
 
335
- logScope(2, "\nLogging Domain Stats", () => {
341
+ logScope(2, "\nDomain Stats", () => {
336
342
  logAny(2, [{
337
- Parts: { Amount: stats.domainCount },
343
+ Parts: { Amount: stats.partCount },
338
344
  Files: { Amount: stats.fileCount },
345
+ Ignored: { Amount: stats.ignoreCount }
339
346
  }], "table")
340
347
  })
341
348
  }
349
+
342
350
  logScope(1, "\nRecursively Hydrating Parts", hydrateLog => {
343
351
 
344
352
  class SourceMappedFile {
@@ -405,7 +413,7 @@ function ƒ(_) {
405
413
  packAndMap(url) {
406
414
  const sourceFile = this
407
415
  const script = sourceFile.lines.join("\n")
408
- return _.mapping
416
+ return +_.mapping
409
417
  ? script +
410
418
  `
411
419
  //${"#"} sourceMappingURL=data:application/json;charset=utf-8;base64,${btoaUnicode(sourceFile.getMap())}${url
@@ -475,7 +483,7 @@ function ƒ(_) {
475
483
  earlyImageSources = [],
476
484
  partsByHost = {},
477
485
  preHydrationArchive = serialize(_),
478
- hydrateRecursive = (part, domains = []) => {
486
+ hydratePartsRecursive = (part, domains = []) => {
479
487
 
480
488
  let host
481
489
 
@@ -517,7 +525,9 @@ function ƒ(_) {
517
525
  const extendsString = part.manifest.extends ?? "part"
518
526
  const relativeStepsBack = extendsString.match(/\.*$/)[0].length
519
527
  const typename = relativeStepsBack ? `${extendsString.slice(0, -relativeStepsBack)}.${domains.slice(relativeStepsBack - 1).join(".")}` : extendsString.includes(".") ? extendsString : `${extendsString}.abstract.parts`
520
- prototype = hydrateRecursive(typename)
528
+ prototype = hydratePartsRecursive(typename)
529
+ if (!prototype.isAbstract)
530
+ throw new Error(`Hydration Error: parts can only extend abstract parts (${host} tried to extend ${prototype.host}).`)
521
531
  Object.setPrototypeOf(part, prototype)
522
532
  part.define({
523
533
  isAbstract: { value: part.manifest.abstract }
@@ -663,6 +673,8 @@ function ƒ(_) {
663
673
  earlyImageSources.push([part, fn.slice(6)])
664
674
  imageSources.push([part, fn])
665
675
  }
676
+ subjectIndices.set(`${host}/${fn}`, allSubjects.length)
677
+ allSubjects.push([host, fn])
666
678
  }
667
679
  for (const methodID in part.manifest)
668
680
  if (!["extends", "abstract"].includes(methodID))
@@ -691,7 +703,7 @@ function ƒ(_) {
691
703
  part.define({ [identifier]: { value: childPart } })
692
704
  }
693
705
 
694
- hydrateRecursive(childPart, [subdomain, ...domains])
706
+ hydratePartsRecursive(childPart, [subdomain, ...domains])
695
707
 
696
708
  childPart.define({ "..": { value: part } })
697
709
 
@@ -700,10 +712,23 @@ function ƒ(_) {
700
712
  }
701
713
  if (!part.isAbstract) instances.push(part)
702
714
  allParts.push(part)
715
+ subjectIndices.set(host, allSubjects.length)
716
+ allSubjects.push([host])
703
717
  })
704
718
 
705
719
  return part
706
720
  },
721
+ hydrateSubjectOrigins = () => {
722
+ if (environment === "node") {
723
+ const bits = allSubjects.map(([host, fn]) => +subjectOrigins.get(host + (fn ? "/" + fn : "")))
724
+ const bitString = bits.join("")
725
+ compressedSubjectOrigins = encodeSegment(BigInt("0b" + bitString))
726
+ } else {
727
+ const bitString = decodeSegment(compressedSubjectOrigins).toString(2).padStart(allSubjects.length, "0")
728
+ const bits = [...bitString]
729
+ allSubjects.forEach(([host, fn], index) => subjectOrigins.set(host + (fn ? "/" + fn : ""), bits[index] === "1"))
730
+ }
731
+ },
707
732
  countAndSortInheritorsRecursive = part => {
708
733
  if (part.totalInheritors !== undefined)
709
734
  return part.totalInheritors
@@ -718,7 +743,8 @@ function ƒ(_) {
718
743
  return totalInheritors
719
744
  }
720
745
 
721
- hydrateRecursive(_)
746
+ hydratePartsRecursive(_)
747
+ hydrateSubjectOrigins()
722
748
  countAndSortInheritorsRecursive(_.parts.abstract.part)
723
749
  hydrateLog(`\nParts hydrated in ${Date.now() - hydrationStartTime}ms.`)
724
750
 
@@ -753,10 +779,10 @@ function ƒ(_) {
753
779
  })
754
780
 
755
781
  logScope(1, "\nBuilding Part Instances", buildLog => {
756
- const buildStartTime = Date.now()
782
+ const bootStartTime = Date.now()
757
783
  for (const part of instances)
758
784
  part.startBuild()
759
- buildLog(`Parts built in ${Date.now() - buildStartTime}ms.`)
785
+ buildLog(`Parts built in ${Date.now() - bootStartTime}ms.`)
760
786
  })
761
787
 
762
788
  logScope(2, "\nLogging Part Entropy", () => {
@@ -796,7 +822,7 @@ function ƒ(_) {
796
822
 
797
823
  bootLog(`
798
824
  ╭┈┈┈┈┈┈┈┈┈┈┈ BOOT SUCCEEDED ┈┈┈┈┈┈┈┈┈┈┈╮
799
- ┊ Booted in ${(Date.now() - buildStartTime + "ms.").padEnd(27, " ")}┊
825
+ ┊ Booted in ${(Date.now() - bootStartTime + "ms.").padEnd(27, " ")}┊
800
826
  ┊ ┊
801
827
  ┊ End of synchronous script execution. ┊
802
828
  ╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╯
@@ -805,12 +831,12 @@ function ƒ(_) {
805
831
  }
806
832
 
807
833
  ƒ({
808
- verbosity: 1,
834
+ verbosity: "1",
809
835
  // TODO: Fix source mapping bugs.
810
- mapping: false,
836
+ mapping: "0",
811
837
  change: "major",
812
- hangHydration: 0,
813
- haltHydration: false,
838
+ hangHydration: "0",
839
+ haltHydration: "0",
814
840
  defaultApplicationHost: "kireji.app",
815
- port: 3000
841
+ port: "3000"
816
842
  })
package/src/build.js_.js CHANGED
@@ -1,13 +1,6 @@
1
1
  const boilerplate = ƒ.toString()
2
-
3
2
  const sourceFile = new SourceMappedFile("../", undefined, "build.js")
4
3
  sourceFile.addSection(boilerplate, sourceFile.addSource("build.js", boilerplate))
5
- sourceFile.addSection(`\nƒ(${JSON.stringify(_, (k, v) => {
6
- if (typeof v === "bigint")
7
- return v.toString() + "n"
8
-
9
- return v
10
-
11
- }, 1)})`, sourceFile.addSource(property.filename, property.content))
4
+ sourceFile.addSection(`\nƒ(${serialize(_)}, ${serialize(compressedSubjectOrigins)})`, sourceFile.addSource(property.filename, property.content))
12
5
  const script = sourceFile.packAndMap()
13
6
  return script
@@ -1,5 +1,16 @@
1
+ if (!scroller.query)
2
+ throw new ReferenceError(`Scrollers must define a css-style "query" property which can select the parent element of the scroller (from ${scroller.host}).`)
3
+
1
4
  scroller.container = Q(scroller.query + ">scroller-")
5
+
6
+ if (!scroller.container)
7
+ throw new ReferenceError(`Could not find scroller parent element via query "${scroller.query}" (from ${scroller.host}).`)
8
+
2
9
  scroller.scrollBar = Q(scroller.query + ">scroll-bar")
10
+
11
+ if (!scroller.scrollBar)
12
+ throw new ReferenceError(`Could not find scroller components in the element "${scroller.query}" (from ${scroller.host}). Use scroller.wrap to wrap the HTML contents of the element with the scroller's container and include the custom scroll bar HTML (from ${scroller.host}).`)
13
+
3
14
  scroller.thumb = scroller.scrollBar.querySelector("thumb-")
4
15
  scroller.content = Q(scroller.query + ">scroller->scroll-content")
5
16
  scroller.container.scrollTop = scroller.fraction * scroller.container.scrollHeight
@@ -4,7 +4,7 @@ await Promise.all([
4
4
  ])
5
5
 
6
6
  logScope(0, "Finalizing Hydration", log => {
7
- if (_.haltHydration && !production)
7
+ if (+_.haltHydration && !production)
8
8
  warn('Intentionally blocked hydration.')
9
9
  else {
10
10
 
@@ -15,6 +15,8 @@ for (const code of hotKeys.pressed)
15
15
  terminalKeys.add(code.slice(5).toLowerCase())
16
16
  else if (/^(Minus|Equal|Semicolon|Quote|Comma|Period|Slash|Backquote|Backslash|Bracket(Right|Left)|Arrow(Up|Down|Left|Right)|Escape|Tab|Enter|Backspace)$/.test(code))
17
17
  terminalKeys.add(code.toLowerCase())
18
+ else if (!production && code === "F8")
19
+ debugger
18
20
  else debug("Unhandled Key Code: " + code)
19
21
 
20
22
  const combo = [...terminalKeys].sort()
@@ -100,7 +100,6 @@ logScope(1, `\nCreating Deployment Artifact`, log => {
100
100
  if (!existsSync(archiveFolder))
101
101
  mkdirSync(archiveFolder)
102
102
 
103
- debug()
104
103
  writeFileSync(artifactPath, _["build.js"])
105
104
 
106
105
  log("Success.")
@@ -200,7 +199,7 @@ const httpServer = require('http').createServer((request, response) => logServer
200
199
  /** @type {IVersionedExports} */
201
200
  let destinationExports
202
201
  try {
203
- destinationExports = destinationVersion === _.version ? currentExports : require(`../.versions/${destinationVersion}.js`)
202
+ destinationExports = destinationVersion === _.version ? currentExports : require(`../../../.versions/${destinationVersion}.js`)
204
203
  } catch {
205
204
  throw `Bad Version: ${destinationVersion}`
206
205
  }
@@ -212,7 +211,7 @@ const httpServer = require('http').createServer((request, response) => logServer
212
211
  /** @type {IVersionedExports} */
213
212
  let sourceExports
214
213
  try {
215
- sourceExports = require(`../.versions/${sourceVersion}.js`)
214
+ sourceExports = require(`../../../.versions/${sourceVersion}.js`)
216
215
  } catch {
217
216
  throw `Bad Version: ${sourceVersion}`
218
217
  }
@@ -261,4 +260,4 @@ const httpServer = require('http').createServer((request, response) => logServer
261
260
  }
262
261
  ))
263
262
 
264
- httpServer.listen(_.port, () => logScope(0, `Server Ready - http://localhost:${_.port}`))
263
+ httpServer.listen(+_.port, () => logScope(0, `Server Ready - http://localhost:${_.port}`))
@@ -57,13 +57,13 @@ task-bar button,
57
57
  task-bar menu-button {
58
58
  height: var(--icon-size);
59
59
  line-height: var(--icon-size);
60
- cursor: pointer;
61
60
  font-size: calc(var(--icon-size) * 1.2);
62
61
  }
63
62
 
64
63
  button:hover,
65
64
  menu-button:hover {
66
65
  color: var(--accent);
66
+ cursor: pointer;
67
67
  }
68
68
 
69
69
  menu-button>img {
@@ -70,7 +70,7 @@ pointer.handle({
70
70
  desktopIcons.setRouteID(0n)
71
71
 
72
72
  const application = desktopIcons.superset[Number(TARGET_ELEMENT.getAttribute("data-index"))]
73
- const targetLocation = (_.local ? `http://${application.host}.localhost:${_.port}` : `https://${application.host}`) + encodePathname(_.routeID)
73
+ const targetLocation = (+_.local ? `http://${application.host}.localhost:${_.port}` : `https://${application.host}`) + encodePathname(_.routeID)
74
74
  location = targetLocation
75
75
  },
76
76
  getIntersectionMask() {
package/src/point.js CHANGED
@@ -10,7 +10,7 @@ pointer.handle({
10
10
  if (host !== _.application.host) {
11
11
  if (host in _.applications) {
12
12
  if (pathname === "/" && !hash) {
13
- const targetLocation = (_.local ? `http://${host}.localhost:${_.port}` : `https://${host}`) + encodePathname(_.routeID)
13
+ const targetLocation = (+_.local ? `http://${host}.localhost:${_.port}` : `https://${host}`) + encodePathname(_.routeID)
14
14
  location = targetLocation
15
15
  } else warn(`Cross-host navigation and canonical pathname are not yet handled because of ambiguity between simply changing applications and linking to a canonical home page (while attempting to navigate to "${TARGET_ELEMENT.href}").`)
16
16
  } else window.open(TARGET_ELEMENT.href, '_blank')
package/src/type.d.ts CHANGED
@@ -289,7 +289,17 @@ declare function randomBits(bigCount: number): bigint
289
289
  declare function randomRouteID(cardinality: bigint): bigint
290
290
  /** Returns a random boolean value. */
291
291
  declare function flipCoin(): boolean
292
- /** The immutable list of runtime instances for the root space, in order of when they were fully hydrated. */
292
+ /** The immutable list of runtime instances for the root space, in order of when the were reached during recursive part hydration. */
293
293
  declare const instances: IPartAny[]
294
- /** The immutable list of every part in the root space, in order of when they were fully hydrated. */
295
- declare const allParts: IPartAny[]
294
+ /** The immutable list of every part in the root space, in order of when the were reached during recursive part hydration. */
295
+ declare const allParts: IPartAny[]
296
+ /** The immutable list of every part host and filename combination, in order of when the were reached during recursive part hydration. */
297
+ declare const allSubjects: [host: string, filename?: string]
298
+ /** The Unix timestamp (in ms) when the current instance of the framework started booting. */
299
+ declare const bootStartTime: number
300
+ /** A map that provides information about whether a subject was defined in the project repository (maps to true) or only in the kireji framework package (maps to false). */
301
+ declare const subjectOrigins: Map<string, boolean>
302
+ /** A map that provides the index (in the `allSubjects` array) for the given subject. */
303
+ declare const subjectIndices: Map<string, boolean>
304
+ /** A base64-encoded string which represents a portable bitmask that compresses `subjectOrigins` in hydration order (because that order is identical between environments). */
305
+ declare const compressedSubjectOrigins: string