arcane-os 0.27.1 → 0.28.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.28.0
4
+
5
+ - Expose existing runtime modules and entities directly through
6
+ `arcane-os/modules/<filename>` and `arcane-os/entities/<filename>`, including
7
+ extensions. Package exports and managed maps point to the actual files, so
8
+ relative imports, component resources and `import.meta.resolve` keep their
9
+ established directory and lifecycle. Existing lowercase exports remain.
10
+ - Root apps using direct npm routes generate package-namespaced maps without
11
+ the old `arcane/*` or `./arcane/*` keys. Update authored root-app bare imports
12
+ before regenerating the managed map. Installed `runtime/arcane/` directories
13
+ remain unchanged; physical, virtual and nested layouts retain compatibility.
14
+ - Use the package namespace for generated root app code and shared PWA imports,
15
+ preserving parallel startup and the existing storage and installation owners.
16
+ - New root apps declare the SDK as a runtime dependency. Init promotes their
17
+ SDK development declaration and preserves existing runtime or optional
18
+ classification in every layout, including aliases. Existing nested scaffold
19
+ defaults remain unchanged. No server, listener or application policy changes.
20
+
3
21
  ## 0.27.1
4
22
 
5
23
  - Preserve the previous `apps/<id>/arcane-sw.js` and `arcane-offline.json`
package/README.md CHANGED
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
19
19
  `arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
20
20
  event, cancellation, and browser run contracts.
21
21
 
22
- This checkout defines the `0.27.1` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.28.0` SDK contract. Applications pin one exact npm
23
23
  version and lockfile; registry state is deliberately not baked into application
24
24
  artifacts.
25
25
 
@@ -33,6 +33,12 @@ Select `appsRoot: "."` for a standalone root app, or scaffold one with
33
33
  `arcane new my-app --apps-root .`. After npm installation, `arcane import-map`
34
34
  refreshes managed maps and the root app's static PWA/navigation files for ordinary
35
35
  static hosting. Portable app packages still contain their selected runtime.
36
+ Root/direct maps use `arcane-os/modules/<filename>` and
37
+ `arcane-os/entities/<filename>` (including extensions) plus the focused lowercase
38
+ exports, rather than `arcane/*` aliases. These paths resolve to the actual npm
39
+ files, preserving relative component URLs. New root apps put the SDK in runtime
40
+ `dependencies`; init preserves existing runtime/optional declarations and promotes
41
+ a root app's SDK development declaration without changing its other packages.
36
42
  Existing `apps/<id>`, virtual `/arcane` routes, and materialized workspaces remain
37
43
  supported. Node services continue to
38
44
  use the installed CLI or public imports such as `arcane-os/mail`.
@@ -89,7 +89,7 @@ export function getPwaInstall() {
89
89
 
90
90
  async function loadInstallStorage() {
91
91
  if (!globalThis.dbopfs) {
92
- await import('arcane/DBOPFS');
92
+ await import('arcane-os/modules/DBOPFS.js');
93
93
  }
94
94
  const storage = globalThis.dbopfs;
95
95
  if (!storage) {
@@ -273,8 +273,8 @@ export function mountPwaInstallPrompt({appName = ''} = {}) {
273
273
  if (!globalThis.document) return null;
274
274
  // Both modules may start independently; saved theme loading is not a barrier.
275
275
  await Promise.all([
276
- import('arcane/HTMLImport'),
277
- import('arcane/ThemeBootstrap')
276
+ import('arcane-os/modules/HTMLImport.js'),
277
+ import('arcane-os/modules/ThemeBootstrap.js')
278
278
  ]);
279
279
  if (owner.state.status === 'disposed') return null;
280
280
  if (!document.body) {
@@ -295,7 +295,7 @@ export function mountPwaInstallPrompt({appName = ''} = {}) {
295
295
  host.hidden = true;
296
296
  host.dataset.appName = String(appName);
297
297
  host.dataset.arcanePwaInstall = '';
298
- host.setAttribute('href', new URL('../components/pwa-install.html', import.meta.resolve('arcane/HTMLImport')).href);
298
+ host.setAttribute('href', new URL('../components/pwa-install.html', import.meta.resolve('arcane-os/modules/HTMLImport.js')).href);
299
299
  return new Promise(function waitForInstallComponent(resolve, reject) {
300
300
  const observer = new MutationObserver(function observeRemovedInstallComponent() {
301
301
  if (!host.isConnected) cancelMount();
@@ -156,7 +156,7 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
156
156
 
157
157
  async function loadCheckStorage() {
158
158
  if (!globalThis.dbopfs) {
159
- await import('arcane/DBOPFS');
159
+ await import('arcane-os/modules/DBOPFS.js');
160
160
  }
161
161
  const storage = globalThis.dbopfs;
162
162
  if (!storage) {
@@ -7,6 +7,10 @@
7
7
  "moduleSystem": "ESM"
8
8
  },
9
9
  "memberCount": 210,
10
+ "runtimeSubpathPatterns": {
11
+ "arcane-os/modules/*": "./runtime/arcane/modules/*",
12
+ "arcane-os/entities/*": "./runtime/arcane/entities/*"
13
+ },
10
14
  "members": [
11
15
  {
12
16
  "id": "root:APP_BUNDLE_DESCRIPTOR_NAME",
@@ -146,7 +146,7 @@ all four source and destination paths. Package selection and SDK
146
146
  version come from the installed dependency, not a generated runtime lock.
147
147
 
148
148
  `arcane import-map`, `arcane dev`, packaging, and generated PWA inventories use
149
- the same actual npm destinations. For example, `arcane/HTMLImport` resolves to
149
+ the same actual npm destinations. For example, `arcane-os/modules/HTMLImport.js` resolves to
150
150
  `./node_modules/arcane-os/runtime/arcane/modules/HTMLImport.js`, and
151
151
  `arcane-os/event-manager` resolves to
152
152
  `./node_modules/arcane-os/browser-runtime/event-manager.mjs`. A normal static
@@ -180,6 +180,20 @@ query strings and fragments. It preserves authored files at those destinations.
180
180
  For a direct-installed root PWA it generates the static PWA records at the root,
181
181
  so normal static hosting needs no SDK request handler or runtime copy.
182
182
 
183
+ Direct-installed root maps expose `arcane-os/modules/<filename>` and
184
+ `arcane-os/entities/<filename>` (including extensions), plus the existing
185
+ focused lowercase package exports. They omit the old `arcane/*` and
186
+ `./arcane/*` keys. Real installed `runtime/arcane/` directories are unchanged.
187
+ Physical, nested and virtual layouts retain their established aliases and also
188
+ support the package-namespaced module/entity keys. Relative module and component
189
+ URLs still resolve from the actual selected file, not a reexport wrapper.
190
+
191
+ New root apps declare the SDK in `dependencies` because serving their npm
192
+ resources requires the installed package at runtime. Root `init` promotes an
193
+ existing SDK development declaration while preserving other dependencies.
194
+ Existing runtime or optional declarations retain their classification across
195
+ layouts; the default nested scaffold continues to use its development dependency.
196
+
183
197
  ## Installed SDK runtime materialization
184
198
 
185
199
  `materializeInstalledSdkRuntime()` is the Node entrypoint for refreshing one
@@ -5,11 +5,16 @@ The npm package exposes a Node.js ESM control plane, the portable
5
5
  `arcane-os/preference-store`, `arcane-os/speech-playback`,
6
6
  `arcane-os/speech-text`, `arcane-os/ai/tool-text-stream`, and `arcane-os/browser-device` entrypoints, and the browser-only
7
7
  `arcane-os/pwa`, `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
8
+ The `arcane-os/modules/<filename>` and `arcane-os/entities/<filename>` paths
9
+ resolve directly to the existing runtime files, with their actual extension.
10
+ They are public Node package exports and managed browser keys, without a wrapper.
11
+ For example, `import.meta.resolve('arcane-os/modules/WaitForComponent.js')`
12
+ retains the module directory needed to resolve `../components/modal.html`.
8
13
  Lowercase runtime subpaths also expose existing module namespaces through both
9
14
  Node package exports and managed browser imports, as listed below. Established
10
15
  browser names such as `arcane/AIProviderRuntime`, `arcane/AIRuntimeState`, and
11
- `arcane/ThemeBootstrap` remain managed-map names rather than Node package
12
- entrypoints. Applications call `globalThis.Arcane` for capability-gated host
16
+ `arcane/ThemeBootstrap` remain compatibility names in physical/nested maps;
17
+ direct-installed root maps use the package namespace instead. Applications call `globalThis.Arcane` for capability-gated host
13
18
  behavior.
14
19
 
15
20
  This page is the canonical inventory for every JavaScript name reachable through `package.json#exports`. The same binding can appear at the root and a focused subpath; those entrypoints are listed together. The root workspace `discoverApps` and the low-level packager `discoverApps` are intentionally separate records because they are different functions.
@@ -36,6 +41,8 @@ runtime layouts.
36
41
  | `arcane-os/packager` | Low-level browser app packager. |
37
42
  | `arcane-os/release-bundle` | Deterministic external release bundles. |
38
43
  | `arcane-os/event-manager` | Central synchronous events, complete time-travel history, playback, and optional DOM instrumentation. |
44
+ | `arcane-os/modules/<filename>` | Direct existing runtime module, including its `.js` or `.mjs` extension. |
45
+ | `arcane-os/entities/<filename>` | Direct existing runtime entity, including its extension. |
39
46
  | `arcane-os/logging` | Shared console diagnostics controlled by the existing `user.developer` preference. |
40
47
  | `arcane-os/preference-store` | Portable preference records and injected storage adapters. |
41
48
  | `arcane-os/ai` | Existing `AI.js` provider-neutral inference and speech namespace. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.27.1",
3
+ "version": "0.28.0",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -13,6 +13,8 @@
13
13
  "./toolchain": "./src/toolchain.mjs",
14
14
  "./events": "./src/events.mjs",
15
15
  "./event-manager": "./src/event-manager.mjs",
16
+ "./modules/*": "./runtime/arcane/modules/*",
17
+ "./entities/*": "./runtime/arcane/entities/*",
16
18
  "./logging": "./browser-runtime/logging.mjs",
17
19
  "./browser-device": "./browser-runtime/browser-device.mjs",
18
20
  "./pwa": "./browser-runtime/pwa.mjs",
@@ -1390,6 +1390,9 @@ export async function buildImportMap({files,signal,version=SDK_VERSION,encodePat
1390
1390
  for(const relative of [...inventory].sort(compareText)){
1391
1391
  if(JAVASCRIPT_EXTENSION.test(relative)){
1392
1392
  const target=runtimeTarget(relative);
1393
+ if(relative.startsWith('modules/')||relative.startsWith('entities/')){
1394
+ registerSpecifier(namedRegistry,`arcane-os/${relative}`,target);
1395
+ }
1393
1396
  registerSpecifier(namedRegistry,target,target);
1394
1397
  }
1395
1398
  }
@@ -1453,7 +1456,7 @@ async function physicalRuntime(workspaceRoot,signal){
1453
1456
  return {files};
1454
1457
  }
1455
1458
 
1456
- async function managedImportMapBuild(resolvedWorkspace,signal,pwaEnabled=false){
1459
+ async function managedImportMapBuild(resolvedWorkspace,resolvedApp,signal,pwaEnabled=false){
1457
1460
  const installed=await readInstalledSdkLayout(resolvedWorkspace);
1458
1461
  const [runtime,version]=await Promise.all([
1459
1462
  installed?installedRuntimeFiles(resolvedWorkspace,installed,signal):physicalRuntime(resolvedWorkspace,signal),
@@ -1462,13 +1465,15 @@ async function managedImportMapBuild(resolvedWorkspace,signal,pwaEnabled=false){
1462
1465
  const built=await buildImportMap({files:runtime.files,signal,version});
1463
1466
  if(installed?.direct){
1464
1467
  const imports={};
1468
+ const rootApplication=samePath(resolvedWorkspace,resolvedApp);
1465
1469
  function installedBrowserUrl(value){
1466
1470
  return value.startsWith('./')
1467
1471
  ?`./${installedRuntimeTarget(value.slice(2),installed,{browser:true})}`:value;
1468
1472
  }
1469
1473
  for(const [specifier,target] of Object.entries(built.imports)){
1474
+ if(rootApplication&&specifier.startsWith('arcane/'))continue;
1470
1475
  const selected=installedBrowserUrl(target);
1471
- imports[specifier]=selected;
1476
+ if(!rootApplication)imports[specifier]=selected;
1472
1477
  // Relative module imports and bare names must resolve to the same instance.
1473
1478
  imports[installedBrowserUrl(specifier)]=selected;
1474
1479
  }
@@ -2846,7 +2851,7 @@ async function generateImportMapUnlocked({
2846
2851
  }catch(error){
2847
2852
  if(error?.code!=='ENOENT')throw error;
2848
2853
  }
2849
- const {built,json,version}=await managedImportMapBuild(resolvedWorkspace,signal,pwaEnabled);
2854
+ const {built,json,version}=await managedImportMapBuild(resolvedWorkspace,resolvedApp,signal,pwaEnabled);
2850
2855
  const renderedDocuments=documentStates.map(item=>({
2851
2856
  ...item,
2852
2857
  content:rewriteAssetReferences(renderManagedHtml(item.html,json,item.baseHref),{
package/src/scaffold.mjs CHANGED
@@ -192,21 +192,26 @@ async function prepareExistingPackage(workspaceRoot,files,existingPackage,sdkDec
192
192
  if(conflicts.length){
193
193
  fail(`Existing package.json conflicts with Arcane setup; no values were overwritten. Resolve: ${conflicts.join('; ')}.`);
194
194
  }
195
+ const dependencyGroup=sdkDeclaration.dependencyGroup==='optionalDependencies'
196
+ ?'optionalDependencies'
197
+ :generated.dependencies||sdkDeclaration.dependencyGroup==='dependencies'
198
+ ?'dependencies':'devDependencies';
195
199
  const merged={
196
200
  ...existing,
197
201
  private:true,
198
202
  type:'module',
199
203
  scripts:{...generated.scripts,...(existing.scripts||{})},
200
- devDependencies:{
201
- ...(existing.devDependencies||{}),
204
+ [dependencyGroup]:{
205
+ ...(existing[dependencyGroup]||{}),
202
206
  [sdkDeclaration.dependencyName]:sdkDeclaration.specifier
203
207
  },
204
208
  engines:{...generated.engines,...(existing.engines||{})}
205
209
  };
206
- if(existing.dependencies?.[sdkDeclaration.dependencyName]!==undefined){
207
- merged.dependencies={...existing.dependencies};
208
- delete merged.dependencies[sdkDeclaration.dependencyName];
209
- if(Object.keys(merged.dependencies).length===0)delete merged.dependencies;
210
+ if(dependencyGroup!=='devDependencies'
211
+ &&existing.devDependencies?.[sdkDeclaration.dependencyName]!==undefined){
212
+ merged.devDependencies={...existing.devDependencies};
213
+ delete merged.devDependencies[sdkDeclaration.dependencyName];
214
+ if(Object.keys(merged.devDependencies).length===0)delete merged.devDependencies;
210
215
  }
211
216
  if(JSON.stringify(existing)===JSON.stringify(merged)){
212
217
  return {exists:true,updated:false};
@@ -159,7 +159,7 @@ appropriate.
159
159
 
160
160
  - Use plain JavaScript, HTML, and CSS; do not introduce TypeScript or TSX.
161
161
  - Keep reusable portable mechanisms in the Arcane SDK and app-specific behavior under \`${appPrefix||'./'}\`.
162
- - Keep \`${runtimePrefix}/css/theme.css\` before app styles and import \`arcane/ThemeBootstrap\` before app code runs.
162
+ - Keep \`${runtimePrefix}/css/theme.css\` before app styles and import \`${directRuntime?'arcane-os/modules/ThemeBootstrap.js':'arcane/ThemeBootstrap'}\` before app code runs.
163
163
  - Use \`rgb(...)\` or \`rgba(...)\` for new CSS colors.
164
164
  - Build one named app and one explicit target at a time. Native targets may be unavailable until their adapters are installed.
165
165
  - Preserve complete application, model, document, message, log, diagnostic, process, and tool content. Do not truncate, clip, tail, elide, or silently discard it.
@@ -181,7 +181,7 @@ npm run dev
181
181
 
182
182
  Open the loopback URL printed by the development server. ${directRuntime?'This root app reads SDK files directly from its installed npm package; an ordinary static host uses the same resource paths.':'This app uses the existing physical arcane/ runtime layout.'} The SDK server does not expose an Ollama HTTP endpoint.
183
183
 
184
- Commit the generated \`package-lock.json\` after dependency installation. CI intentionally uses \`npm ci\` and therefore requires that lock. Before the SDK is published, install a locally packed \`${SDK_NAME}\` \`.tgz\` with \`npm install --save-dev --save-exact <path-to-tarball>\`; keep that tarball at the lock file's relative path for repeatable local \`npm ci\` runs.
184
+ Commit the generated \`package-lock.json\` after dependency installation. CI intentionally uses \`npm ci\` and therefore requires that lock. ${directRuntime?'The SDK is a runtime dependency: keep it installed when serving this app directly from node_modules. ':''}Before the SDK is published, install a locally packed \`${SDK_NAME}\` \`.tgz\` with \`npm install ${directRuntime?'--save-prod':'--save-dev'} --save-exact <path-to-tarball>\`; keep that tarball at the lock file's relative path for repeatable local \`npm ci\` runs.
185
185
 
186
186
  ## Optional browser release commands
187
187
 
@@ -199,7 +199,7 @@ import map in every directly navigable descriptor-admitted \`.html\`/\`.htm\`
199
199
  document. HTML component fragments remain package files but do not receive a
200
200
  document-level base or managed import map.
201
201
  Development, package, and build refresh that shared inventory when the selected operation needs it.
202
- Named \`arcane/*\` imports resolve through the managed map to the selected SDK files. Packaging copies the complete selected application, runtime, and specifier
202
+ Named \`${directRuntime?'arcane-os/modules/* and arcane-os/entities/*':'arcane/*'}\` imports resolve through the managed map to the selected SDK files. Packaging copies the complete selected application, runtime, and specifier
203
203
  map to \`dist/${appId}\` without running application tests. Run \`verify\` only when
204
204
  the user explicitly selects verification or a release artifact that requires it;
205
205
  \`bundle\` creates the distributable archive and \`run\` launches the selected
@@ -231,7 +231,7 @@ Every browser release also carries Arcane OS licensing material under \`${direct
231
231
  'run:browser':'arcane run --target browser'
232
232
  }:{})
233
233
  },
234
- devDependencies:{
234
+ [directRuntime?'dependencies':'devDependencies']:{
235
235
  [sdkDependencyName]:sdkDependencySpecifier
236
236
  },
237
237
  engines:{node:'>=22.23.2'}
@@ -369,9 +369,11 @@ ${bootstrapMarkup}</head>
369
369
  files.set(`${appPrefix}modules/arcane.importmap.json`,json({imports:{}}));
370
370
  }
371
371
  const themeSpecifier=namedImports
372
- ?'arcane/ThemeBootstrap':`${appsRoot==='.'?'../':'../../../'}${runtimePrefix.slice(2)}/modules/ThemeBootstrap.js`;
372
+ ?directRuntime?'arcane-os/modules/ThemeBootstrap.js':'arcane/ThemeBootstrap'
373
+ :`${appsRoot==='.'?'../':'../../../'}${runtimePrefix.slice(2)}/modules/ThemeBootstrap.js`;
373
374
  const appDataSpecifier=namedImports
374
- ?'arcane/AppDataScope':`${appsRoot==='.'?'../':'../../../'}${runtimePrefix.slice(2)}/modules/AppDataScope.js`;
375
+ ?directRuntime?'arcane-os/app-data-scope':'arcane/AppDataScope'
376
+ :`${appsRoot==='.'?'../':'../../../'}${runtimePrefix.slice(2)}/modules/AppDataScope.js`;
375
377
  const strongTypeSpecifier=namedImports
376
378
  ?'strong-type':directRuntime?`../${sdkPackageSource}/runtime/strong-type/index.js`:'../../../arcane/dependencies/strong-type/index.js';
377
379
  files.set(`${appPrefix}modules/App.js`,`import Is from '${strongTypeSpecifier}';
package/src/workspace.mjs CHANGED
@@ -117,7 +117,7 @@ export function resolveSdkPackageDeclaration(rootPackage,{
117
117
  }={}){
118
118
  if(!isObject(rootPackage))fail('package.json must contain a JSON object.');
119
119
  const candidates=[];
120
- for(const groupName of ['devDependencies','dependencies']){
120
+ for(const groupName of ['devDependencies','dependencies','optionalDependencies']){
121
121
  const group=rootPackage[groupName];
122
122
  if(group===undefined)continue;
123
123
  if(!isObject(group))fail(`package.json ${groupName} must be a JSON object.`);