arcane-os 0.1.0-dev.5 → 0.1.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.
@@ -1,4 +1,8 @@
1
1
  import {SDK_NAME,SDK_VERSION} from '../constants.mjs';
2
+ import {
3
+ SDK_BROWSER_RUNTIME_CONTENT_SHA256,
4
+ SDK_BROWSER_RUNTIME_MANIFEST_SHA256
5
+ } from '../sdk-browser-runtime.mjs';
2
6
 
3
7
  function json(value){
4
8
  return `${JSON.stringify(value,null,2)}\n`;
@@ -23,7 +27,9 @@ export function workspaceTemplate({
23
27
  appId,
24
28
  displayName,
25
29
  runtimeRelease,
30
+ sdkBrowserRuntimeRelease,
26
31
  appOnly=false,
32
+ namedImports=true,
27
33
  minimumCoreVersion='0.8.12',
28
34
  target='browser',
29
35
  appIcon
@@ -42,6 +48,23 @@ export function workspaceTemplate({
42
48
  const packageName=`arcane-${appId}`;
43
49
  const runtimeContentSha256=runtimeRelease?.contentSha256;
44
50
  const upstreamCommit=runtimeRelease?.source?.commit;
51
+ const browserManifestSha256=sdkBrowserRuntimeRelease?.manifestSha256;
52
+ const browserContentSha256=sdkBrowserRuntimeRelease?.contentSha256;
53
+ const browserSource=sdkBrowserRuntimeRelease?.source;
54
+ const expectedBrowserDependencies=[
55
+ {
56
+ name:'event-pubsub',
57
+ version:'6.1.0',
58
+ resolved:'https://registry.npmjs.org/event-pubsub/-/event-pubsub-6.1.0.tgz',
59
+ integrity:'sha512-FEMlhTxwqGM0hztTixG6FhVFXqp7Eq1ltk5mSreK6Mhy3xWWpLAzEUR6OMvMdNqT3jgSxA8JDhnhyAG3X4Xy7Q=='
60
+ },
61
+ {
62
+ name:'strong-type',
63
+ version:'2.0.0',
64
+ resolved:'https://registry.npmjs.org/strong-type/-/strong-type-2.0.0.tgz',
65
+ integrity:'sha512-HHrY9qYC7yn+5mlewiI3k9RQM9gZqGQsqbomZcd10Ks0h4RlX01nnkWbCe4AsVPCI6KaFvpkWm1nHMD+Ykup6g=='
66
+ }
67
+ ];
45
68
  const packageInclude=[`${appId}.css`,'index.html'];
46
69
  if(native)packageInclude.push('img/icon.png');
47
70
  packageInclude.push('manifest.json','modules');
@@ -72,7 +95,36 @@ appropriate.
72
95
  if(!appOnly&&(typeof upstreamCommit!=='string'||!/^[a-f0-9]{40}$/.test(upstreamCommit))){
73
96
  throw new Error('The SDK runtime release does not contain a valid upstream commit.');
74
97
  }
98
+ if(!appOnly&&(
99
+ browserManifestSha256!==SDK_BROWSER_RUNTIME_MANIFEST_SHA256
100
+ ||browserContentSha256!==SDK_BROWSER_RUNTIME_CONTENT_SHA256
101
+ ||sdkBrowserRuntimeRelease?.builder!=='arcane-sdk-browser-runtime-v1'
102
+ ||sdkBrowserRuntimeRelease?.sdkVersion!==SDK_VERSION
103
+ ||browserSource?.protocol!=='arcane-sdk-browser-runtime/1'
104
+ ||browserSource?.authority!=='arcane-os-sdk'
105
+ ||browserSource?.repository!=='https://github.com/TheWizardNexus/arcane-os-sdk.git'
106
+ ||browserSource?.browserEntry!=='arcane-os/event-manager'
107
+ ||!Array.isArray(browserSource?.dependencies)
108
+ ||browserSource.dependencies.length!==expectedBrowserDependencies.length
109
+ ||browserSource.dependencies.some((actual,index)=>{
110
+ const expected=expectedBrowserDependencies[index];
111
+ return !actual||Object.keys(actual).sort().join('\0')
112
+ !==['name','version','resolved','integrity'].sort().join('\0')
113
+ ||actual.name!==expected.name||actual.version!==expected.version
114
+ ||actual.resolved!==expected.resolved||actual.integrity!==expected.integrity;
115
+ })
116
+ )){
117
+ throw new Error('The SDK browser runtime release does not contain a valid trusted identity.');
118
+ }
75
119
 
120
+ const importMapMarkup=namedImports?` <script type="importmap" data-arcane-import-map>
121
+ {
122
+ "imports": {}
123
+ }
124
+ </script>
125
+ `:'';
126
+ const bootstrapMarkup=namedImports?'':
127
+ ' <script type="module" src="./arcane/modules/ThemeBootstrap.js?v=1"></script>\n';
76
128
  const files=new Map();
77
129
  files.set('.gitignore',[
78
130
  'node_modules/',
@@ -96,7 +148,7 @@ appropriate.
96
148
 
97
149
  - Use plain JavaScript, HTML, and CSS; do not introduce TypeScript or TSX.
98
150
  - Keep reusable mechanisms in Arcane OS and app-specific behavior under \`apps/${appId}/\`.
99
- - Keep \`arcane/css/theme.css\` and \`ThemeBootstrap.js\` loaded before app styles and modules.
151
+ - Keep \`arcane/css/theme.css\` before app styles and import \`arcane/ThemeBootstrap\` before app code runs.
100
152
  - Use \`rgb(...)\` or \`rgba(...)\` for new CSS colors.
101
153
  - Build one named app and one explicit target at a time. Native targets may be unavailable until their adapters are installed.
102
154
  - Run \`npm run check\` before committing.
@@ -113,20 +165,32 @@ npm run doctor
113
165
  npm run dev
114
166
  \`\`\`
115
167
 
116
- Open the loopback URL printed by the development server. The source server exposes this app and the SDK's browser runtime; it does not expose an Ollama HTTP endpoint.
168
+ Open the loopback URL printed by the development server. The source server exposes this app and its authenticated physical \`arcane/\` runtime; it does not expose an Ollama HTTP endpoint.
117
169
 
118
170
  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.
119
171
 
120
- ## Check and package
172
+ ## Check and release the browser package
121
173
 
122
174
  \`\`\`sh
123
175
  npm run check
176
+ npm run import-map
124
177
  npm run package
178
+ npm run verify
179
+ npm run bundle
125
180
  npm run run
126
181
  \`\`\`
127
182
 
128
- The browser release is written to \`dist/${appId}\`. Native providers accept only
129
- explicit targets and fail honestly when their platform toolchain is unavailable.
183
+ The explicit \`import-map\` command refreshes
184
+ \`apps/${appId}/modules/arcane.importmap.json\` and the managed inline browser
185
+ import map in \`index.html\`. Development, package, and build refresh it
186
+ automatically. Named \`arcane/*\` imports resolve against the physical workspace
187
+ \`arcane/\` tree. Packaging copies the same authenticated bytes and specifier map
188
+ to \`dist/${appId}\`, \`verify\` authenticates that directory, \`bundle\` creates
189
+ its distributable archive, and \`run\` launches the verified packaged browser
190
+ release.
191
+
192
+ Native targets are provider-supplied and must be scaffolded and selected
193
+ explicitly; this browser workflow does not imply a standalone native executable.
130
194
  ${nativeGuide}
131
195
 
132
196
  Every browser release also carries Arcane OS licensing material under \`licenses/arcane-os/\`. Review those terms before distribution.
@@ -140,11 +204,16 @@ Every browser release also carries Arcane OS licensing material under \`licenses
140
204
  dev:'arcane dev',
141
205
  test:'arcane test',
142
206
  check:'arcane check',
207
+ 'import-map':'arcane import-map',
143
208
  package:'arcane package',
209
+ verify:'arcane verify',
210
+ bundle:'arcane bundle',
144
211
  build:`arcane build --target ${buildTarget}`,
145
212
  run:`arcane run --target ${runTarget}`,
146
- 'build:browser':'arcane build --target browser',
147
- 'run:browser':'arcane run --target browser'
213
+ ...(native?{
214
+ 'build:browser':'arcane build --target browser',
215
+ 'run:browser':'arcane run --target browser'
216
+ }:{})
148
217
  },
149
218
  devDependencies:{
150
219
  [SDK_NAME]:SDK_VERSION
@@ -158,15 +227,9 @@ Every browser release also carries Arcane OS licensing material under \`licenses
158
227
  sharedPayloads:{
159
228
  'browser-runtime':[
160
229
  {
161
- source:'node_modules/arcane-os/runtime/arcane',
230
+ source:'arcane',
162
231
  destination:'arcane',
163
- include:['components','css','entities','img','modules','security'],
164
- exclude:[]
165
- },
166
- {
167
- source:'node_modules/arcane-os/runtime/strong-type',
168
- destination:'node_modules/strong-type',
169
- include:['index.js','licence','package.json'],
232
+ include:['components','css','dependencies','entities','img','modules','sdk','security'],
170
233
  exclude:[]
171
234
  },
172
235
  {
@@ -186,6 +249,14 @@ Every browser release also carries Arcane OS licensing material under \`licenses
186
249
  contentSha256:runtimeContentSha256,
187
250
  upstreamCommit
188
251
  },
252
+ sdkBrowserRuntime:{
253
+ manifest:'node_modules/arcane-os/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json',
254
+ manifestSha256:browserManifestSha256,
255
+ contentSha256:browserContentSha256,
256
+ builder:sdkBrowserRuntimeRelease?.builder,
257
+ sdkVersion:sdkBrowserRuntimeRelease?.sdkVersion,
258
+ source:sdkBrowserRuntimeRelease?.source
259
+ },
189
260
  protocols:{
190
261
  arcane:'arcane/1',
191
262
  cliEvents:'arcane-cli-events/1',
@@ -288,15 +359,14 @@ jobs:
288
359
  <meta charset="utf-8">
289
360
  <meta name="arcane-app-id" content="${html(appId)}">
290
361
  <base href="../../">
291
- <meta name="viewport" content="width=device-width, initial-scale=1">
362
+ ${importMapMarkup} <meta name="viewport" content="width=device-width, initial-scale=1">
292
363
  <meta name="theme-color" content="rgb(23, 34, 56)">
293
364
  <title>${html(name)}</title>
294
365
  <link rel="manifest" href="./apps/${html(appId)}/manifest.json">
295
366
  <link rel="stylesheet" href="./arcane/css/theme.css?v=1">
296
367
  <link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
297
368
  <link rel="stylesheet" href="./apps/${html(appId)}/${html(appId)}.css?v=1">
298
- <script type="module" src="./arcane/modules/ThemeBootstrap.js?v=1"></script>
299
- </head>
369
+ ${bootstrapMarkup}</head>
300
370
  <body>
301
371
  <main class="app-shell">
302
372
  <section class="arcane-card" aria-labelledby="app-title">
@@ -337,12 +407,49 @@ jobs:
337
407
  text-transform: uppercase;
338
408
  }
339
409
  `);
340
- files.set(`apps/${appId}/modules/App.js`,`const appName=${JSON.stringify(name)};
410
+ if(namedImports){
411
+ files.set(`apps/${appId}/modules/arcane.importmap.json`,json({imports:{}}));
412
+ }
413
+ const themeSpecifier=namedImports
414
+ ?'arcane/ThemeBootstrap':'../../../arcane/modules/ThemeBootstrap.js';
415
+ const appDataSpecifier=namedImports
416
+ ?'arcane/AppDataScope':'../../../arcane/modules/AppDataScope.js';
417
+ files.set(`apps/${appId}/modules/App.js`,`import arcaneThemeReady from '${themeSpecifier}';
418
+ import {
419
+ resolveApplicationId,
420
+ resolveApplicationLocalStorageKey
421
+ } from '${appDataSpecifier}';
422
+
423
+ const appName=${JSON.stringify(name)};
341
424
  const action=document.querySelector('#app-action');
342
425
  const status=document.querySelector('#app-status');
343
426
 
427
+ await arcaneThemeReady;
428
+
429
+ const appId=await resolveApplicationId();
430
+ const countKey=resolveApplicationLocalStorageKey('hello-count',{applicationId:appId});
431
+
432
+ function loadHelloCount(){
433
+ try{
434
+ const value=Number(globalThis.localStorage?.getItem(countKey)??0);
435
+ return Number.isSafeInteger(value)&&value>=0?value:0;
436
+ }catch{
437
+ return 0;
438
+ }
439
+ }
440
+
441
+ function saveHelloCount(value){
442
+ try{
443
+ globalThis.localStorage?.setItem(countKey,String(value));
444
+ }catch{
445
+ // The greeting still works when browser persistence is unavailable.
446
+ }
447
+ }
448
+
344
449
  action?.addEventListener('click',()=>{
345
- status.textContent=\`The \${appName} app is working.\`;
450
+ const count=loadHelloCount()+1;
451
+ saveHelloCount(count);
452
+ status.textContent=\`Hello from \${appName}! Greeting \${count}.\`;
346
453
  });
347
454
  `);
348
455
  if(native){
@@ -355,17 +462,22 @@ import test from '${SDK_NAME}/testing';
355
462
  const appRoot=new URL('../',import.meta.url);
356
463
 
357
464
  test('application shell uses the shared Arcane theme in order',async()=>{
358
- const source=await readFile(new URL('index.html',appRoot),'utf8');
465
+ const [source,appSource]=await Promise.all([
466
+ readFile(new URL('index.html',appRoot),'utf8'),
467
+ readFile(new URL('modules/App.js',appRoot),'utf8')
468
+ ]);
359
469
  const theme=source.indexOf('./arcane/css/theme.css');
360
470
  const primitives=source.indexOf('./arcane/css/primitives.css');
361
471
  const appStyle=source.indexOf('./apps/${appId}/${appId}.css');
362
- const bootstrap=source.indexOf('./arcane/modules/ThemeBootstrap.js');
472
+ const importMap=source.indexOf('${namedImports?'data-arcane-import-map':'./arcane/modules/ThemeBootstrap.js'}');
363
473
  const appModule=source.indexOf('./apps/${appId}/modules/App.js');
364
474
 
365
475
  assert.match(source,/<base href="\\.\\.\\/\\.\\.\\/">/);
366
476
  assert.match(source,/<meta name="arcane-app-id" content="${appId}">/);
367
477
  assert.ok(theme>=0&&primitives>theme&&appStyle>primitives);
368
- assert.ok(bootstrap>appStyle&&appModule>bootstrap);
478
+ assert.ok(importMap>=0&&appModule>importMap);
479
+ assert.ok(appSource.includes("from '${themeSpecifier}'"));
480
+ assert.ok(appSource.includes("from '${appDataSpecifier}'"));
369
481
  });
370
482
 
371
483
  test('application package identity matches its directory',async()=>{