arcane-os 0.3.0 → 0.3.2
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 +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,8 +1,4 @@
|
|
|
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';
|
|
6
2
|
|
|
7
3
|
const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
8
4
|
const LOCAL_TARBALL_PATTERN=/^file:.+\.tgz$/iu;
|
|
@@ -29,8 +25,6 @@ function titleCase(appId){
|
|
|
29
25
|
export function workspaceTemplate({
|
|
30
26
|
appId,
|
|
31
27
|
displayName,
|
|
32
|
-
runtimeRelease,
|
|
33
|
-
sdkBrowserRuntimeRelease,
|
|
34
28
|
sdkDependencyName=SDK_NAME,
|
|
35
29
|
sdkDependencySpecifier=SDK_VERSION,
|
|
36
30
|
sdkPackageSource=`node_modules/${sdkDependencyName}`,
|
|
@@ -65,31 +59,6 @@ export function workspaceTemplate({
|
|
|
65
59
|
}
|
|
66
60
|
const name=displayName||`Arcane ${titleCase(appId)}`;
|
|
67
61
|
const packageName=`arcane-${appId}`;
|
|
68
|
-
const runtimeContentSha256=runtimeRelease?.contentSha256;
|
|
69
|
-
const upstreamCommit=runtimeRelease?.source?.legacyProjection?.commit;
|
|
70
|
-
const browserManifestSha256=sdkBrowserRuntimeRelease?.manifestSha256;
|
|
71
|
-
const browserContentSha256=sdkBrowserRuntimeRelease?.contentSha256;
|
|
72
|
-
const browserSource=sdkBrowserRuntimeRelease?.source;
|
|
73
|
-
const expectedBrowserDependencies=[
|
|
74
|
-
{
|
|
75
|
-
name:'event-pubsub',
|
|
76
|
-
version:'6.1.0',
|
|
77
|
-
resolved:'https://registry.npmjs.org/event-pubsub/-/event-pubsub-6.1.0.tgz',
|
|
78
|
-
integrity:'sha512-FEMlhTxwqGM0hztTixG6FhVFXqp7Eq1ltk5mSreK6Mhy3xWWpLAzEUR6OMvMdNqT3jgSxA8JDhnhyAG3X4Xy7Q=='
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name:'strong-type',
|
|
82
|
-
version:'2.0.0',
|
|
83
|
-
resolved:'https://registry.npmjs.org/strong-type/-/strong-type-2.0.0.tgz',
|
|
84
|
-
integrity:'sha512-HHrY9qYC7yn+5mlewiI3k9RQM9gZqGQsqbomZcd10Ks0h4RlX01nnkWbCe4AsVPCI6KaFvpkWm1nHMD+Ykup6g=='
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name:'@wllama/wllama',
|
|
88
|
-
version:'3.6.0',
|
|
89
|
-
resolved:'https://registry.npmjs.org/@wllama/wllama/-/wllama-3.6.0.tgz',
|
|
90
|
-
integrity:'sha512-NN3ZBXqaaUwGXTQubkNvsCaLPjN2XVa0bVS40OYCE8zquYmRc2W3oHYEgwvuSWWDB8aUqTLyMioySCXNkcnD1w=='
|
|
91
|
-
}
|
|
92
|
-
];
|
|
93
62
|
const packageInclude=[`${appId}.css`,'index.html'];
|
|
94
63
|
if(native)packageInclude.push('img/icon.png');
|
|
95
64
|
packageInclude.push('manifest.json','modules');
|
|
@@ -114,34 +83,6 @@ the SDK's license terms; replace it with your own raster application icon when
|
|
|
114
83
|
appropriate.
|
|
115
84
|
`:'';
|
|
116
85
|
|
|
117
|
-
if(!appOnly&&(typeof runtimeContentSha256!=='string'||!/^[a-f0-9]{64}$/.test(runtimeContentSha256))){
|
|
118
|
-
throw new Error('The SDK runtime release does not contain a valid contentSha256.');
|
|
119
|
-
}
|
|
120
|
-
if(!appOnly&&(typeof upstreamCommit!=='string'||!/^[a-f0-9]{40}$/.test(upstreamCommit))){
|
|
121
|
-
throw new Error('The SDK runtime release does not contain a valid upstream commit.');
|
|
122
|
-
}
|
|
123
|
-
if(!appOnly&&(
|
|
124
|
-
browserManifestSha256!==SDK_BROWSER_RUNTIME_MANIFEST_SHA256
|
|
125
|
-
||browserContentSha256!==SDK_BROWSER_RUNTIME_CONTENT_SHA256
|
|
126
|
-
||sdkBrowserRuntimeRelease?.builder!=='arcane-sdk-browser-runtime-v1'
|
|
127
|
-
||sdkBrowserRuntimeRelease?.sdkVersion!==SDK_VERSION
|
|
128
|
-
||browserSource?.protocol!=='arcane-sdk-browser-runtime/1'
|
|
129
|
-
||browserSource?.authority!=='arcane-os-sdk'
|
|
130
|
-
||browserSource?.repository!=='https://github.com/TheWizardNexus/arcane-os-sdk.git'
|
|
131
|
-
||browserSource?.browserEntry!=='arcane-os/event-manager'
|
|
132
|
-
||!Array.isArray(browserSource?.dependencies)
|
|
133
|
-
||browserSource.dependencies.length!==expectedBrowserDependencies.length
|
|
134
|
-
||browserSource.dependencies.some((actual,index)=>{
|
|
135
|
-
const expected=expectedBrowserDependencies[index];
|
|
136
|
-
return !actual||Object.keys(actual).sort().join('\0')
|
|
137
|
-
!==['name','version','resolved','integrity'].sort().join('\0')
|
|
138
|
-
||actual.name!==expected.name||actual.version!==expected.version
|
|
139
|
-
||actual.resolved!==expected.resolved||actual.integrity!==expected.integrity;
|
|
140
|
-
})
|
|
141
|
-
)){
|
|
142
|
-
throw new Error('The SDK browser runtime release does not contain a valid trusted identity.');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
86
|
const importMapMarkup=namedImports?` <script type="importmap" data-arcane-import-map>
|
|
146
87
|
{
|
|
147
88
|
"imports": {}
|
|
@@ -176,28 +117,29 @@ appropriate.
|
|
|
176
117
|
- Keep \`arcane/css/theme.css\` before app styles and import \`arcane/ThemeBootstrap\` before app code runs.
|
|
177
118
|
- Use \`rgb(...)\` or \`rgba(...)\` for new CSS colors.
|
|
178
119
|
- Build one named app and one explicit target at a time. Native targets may be unavailable until their adapters are installed.
|
|
120
|
+
- Preserve complete application, model, document, message, log, diagnostic, process, and tool content. Do not truncate, clip, tail, elide, or silently discard it.
|
|
121
|
+
- Do not make ordinary application behavior depend on byte counts, byte limits, byte identities, hashes, digests, or byte-based admission.
|
|
122
|
+
- Optional hardening must remain inactive unless the user expressly selects secure: true for the exact operation. The ordinary path must remain fully functional.
|
|
179
123
|
- Run tests and checks only when the user explicitly selects verification or a distribution artifact requires it.
|
|
180
124
|
`);
|
|
181
125
|
files.set('README.md',`# ${name}
|
|
182
126
|
|
|
183
|
-
This repository contains the portable Arcane application \`${appId}\`. It
|
|
127
|
+
This repository contains the portable Arcane application \`${appId}\`. It includes the selected SDK runtime for distribution and has no runtime dependency on an Arcane OS source checkout.
|
|
184
128
|
|
|
185
129
|
## Start
|
|
186
130
|
|
|
187
131
|
\`\`\`sh
|
|
188
132
|
npm install
|
|
189
|
-
npm run doctor
|
|
190
133
|
npm run dev
|
|
191
134
|
\`\`\`
|
|
192
135
|
|
|
193
|
-
Open the loopback URL printed by the development server. The source server exposes this app and its
|
|
136
|
+
Open the loopback URL printed by the development server. The source server exposes this app and its physical \`arcane/\` runtime; it does not expose an Ollama HTTP endpoint.
|
|
194
137
|
|
|
195
138
|
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.
|
|
196
139
|
|
|
197
|
-
##
|
|
140
|
+
## Optional browser release commands
|
|
198
141
|
|
|
199
142
|
\`\`\`sh
|
|
200
|
-
npm run check
|
|
201
143
|
npm run import-map
|
|
202
144
|
npm run package
|
|
203
145
|
npm run verify
|
|
@@ -207,12 +149,16 @@ npm run run
|
|
|
207
149
|
|
|
208
150
|
The explicit \`import-map\` command refreshes
|
|
209
151
|
\`apps/${appId}/modules/arcane.importmap.json\` and the managed inline browser
|
|
210
|
-
import map in
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
152
|
+
import map in every directly navigable descriptor-admitted \`.html\`/\`.htm\`
|
|
153
|
+
document. HTML component fragments remain package files but do not receive a
|
|
154
|
+
document-level base or managed import map.
|
|
155
|
+
Development, package, and build refresh that shared inventory when the selected operation needs it.
|
|
156
|
+
Named \`arcane/*\` imports resolve against the physical workspace \`arcane/\`
|
|
157
|
+
tree. Packaging copies the complete selected application, runtime, and specifier
|
|
158
|
+
map to \`dist/${appId}\` without running application tests. Run \`verify\` only when
|
|
159
|
+
the user explicitly selects verification or a release artifact that requires it;
|
|
160
|
+
\`bundle\` creates the distributable archive and \`run\` launches the selected
|
|
161
|
+
packaged browser release.
|
|
216
162
|
|
|
217
163
|
Native targets are provider-supplied and must be scaffolded and selected
|
|
218
164
|
explicitly; this browser workflow does not imply a standalone native executable.
|
|
@@ -254,7 +200,7 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
254
200
|
{
|
|
255
201
|
source:'arcane',
|
|
256
202
|
destination:'arcane',
|
|
257
|
-
include:['components','css','dependencies','entities','img','modules','sdk'
|
|
203
|
+
include:['components','css','dependencies','entities','img','modules','sdk'],
|
|
258
204
|
exclude:[]
|
|
259
205
|
},
|
|
260
206
|
{
|
|
@@ -266,54 +212,6 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
266
212
|
]
|
|
267
213
|
}
|
|
268
214
|
}));
|
|
269
|
-
files.set('arcane.lock.json',json({
|
|
270
|
-
schemaVersion:1,
|
|
271
|
-
sdk:{name:SDK_NAME,version:SDK_VERSION},
|
|
272
|
-
runtime:{
|
|
273
|
-
manifest:`${sdkPackageSource}/runtime/ARCANE_RUNTIME_RELEASE.json`,
|
|
274
|
-
contentSha256:runtimeContentSha256,
|
|
275
|
-
upstreamCommit
|
|
276
|
-
},
|
|
277
|
-
sdkBrowserRuntime:{
|
|
278
|
-
manifest:`${sdkPackageSource}/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json`,
|
|
279
|
-
manifestSha256:browserManifestSha256,
|
|
280
|
-
contentSha256:browserContentSha256,
|
|
281
|
-
builder:sdkBrowserRuntimeRelease?.builder,
|
|
282
|
-
sdkVersion:sdkBrowserRuntimeRelease?.sdkVersion,
|
|
283
|
-
source:sdkBrowserRuntimeRelease?.source
|
|
284
|
-
},
|
|
285
|
-
protocols:{
|
|
286
|
-
arcane:'arcane/1',
|
|
287
|
-
cliEvents:'arcane-cli-events/1',
|
|
288
|
-
targetAdapter:'arcane-target-adapter/1'
|
|
289
|
-
}
|
|
290
|
-
}));
|
|
291
|
-
files.set('.github/workflows/check.yml',`name: check
|
|
292
|
-
|
|
293
|
-
on:
|
|
294
|
-
push:
|
|
295
|
-
branches: [main]
|
|
296
|
-
pull_request:
|
|
297
|
-
|
|
298
|
-
permissions:
|
|
299
|
-
contents: read
|
|
300
|
-
|
|
301
|
-
jobs:
|
|
302
|
-
check:
|
|
303
|
-
runs-on: \${{ matrix.os }}
|
|
304
|
-
strategy:
|
|
305
|
-
fail-fast: false
|
|
306
|
-
matrix:
|
|
307
|
-
os: [ubuntu-latest, windows-latest]
|
|
308
|
-
steps:
|
|
309
|
-
- uses: actions/checkout@v6
|
|
310
|
-
- uses: actions/setup-node@v6
|
|
311
|
-
with:
|
|
312
|
-
node-version: 24
|
|
313
|
-
package-manager-cache: false
|
|
314
|
-
- run: npm ci --ignore-scripts
|
|
315
|
-
- run: npm run check
|
|
316
|
-
`);
|
|
317
215
|
files.set(`apps/${appId}/arcane-app.json`,json({
|
|
318
216
|
schemaVersion:2,
|
|
319
217
|
id:appId,
|
|
@@ -331,15 +229,6 @@ jobs:
|
|
|
331
229
|
exclude:[],
|
|
332
230
|
shared:['browser-runtime']
|
|
333
231
|
},
|
|
334
|
-
permissions:{
|
|
335
|
-
capabilities:[],
|
|
336
|
-
methods:[]
|
|
337
|
-
},
|
|
338
|
-
security:{
|
|
339
|
-
connectOrigins:[],
|
|
340
|
-
frameOrigins:[],
|
|
341
|
-
mediaOrigins:[]
|
|
342
|
-
},
|
|
343
232
|
native:{
|
|
344
233
|
type:'app',
|
|
345
234
|
icon:native?'img/icon.png':null,
|
|
@@ -348,7 +237,7 @@ jobs:
|
|
|
348
237
|
},
|
|
349
238
|
requirements:{
|
|
350
239
|
arcaneProtocol:'arcane/1',
|
|
351
|
-
minimumCoreVersion,
|
|
240
|
+
...(native?{minimumCoreVersion}:{}),
|
|
352
241
|
features:[]
|
|
353
242
|
},
|
|
354
243
|
targets:native?['browser',target].sort():['browser']
|
|
@@ -360,18 +249,13 @@ jobs:
|
|
|
360
249
|
version:'0.1.0',
|
|
361
250
|
entry:'index.html',
|
|
362
251
|
strategy:'static',
|
|
363
|
-
security:{
|
|
364
|
-
connectOrigins:[],
|
|
365
|
-
frameOrigins:[],
|
|
366
|
-
mediaOrigins:[]
|
|
367
|
-
},
|
|
368
252
|
include:packageInclude,
|
|
369
253
|
exclude:[],
|
|
370
254
|
shared:['browser-runtime']
|
|
371
255
|
}));
|
|
372
256
|
files.set(`apps/${appId}/manifest.json`,json({
|
|
373
257
|
name,
|
|
374
|
-
short_name:titleCase(appId)
|
|
258
|
+
short_name:titleCase(appId),
|
|
375
259
|
start_url:'./index.html',
|
|
376
260
|
display:'standalone',
|
|
377
261
|
background_color:'rgb(13, 18, 32)',
|
package/src/testing-loader.mjs
CHANGED
|
@@ -1,13 +1,177 @@
|
|
|
1
|
+
import {lstatSync,realpathSync} from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
|
|
1
5
|
const TESTING_SPECIFIER='arcane-os/testing';
|
|
2
6
|
const TESTING_URL=new URL('./testing.mjs',import.meta.url).href;
|
|
3
7
|
const RUNTIME_STRONG_TYPE_SPECIFIER='../../node_modules/strong-type/index.js';
|
|
4
8
|
const RUNTIME_ARCANE_URL=new URL('../runtime/arcane/',import.meta.url).href;
|
|
5
9
|
const STRONG_TYPE_URL=new URL('../runtime/strong-type/index.js',import.meta.url).href;
|
|
10
|
+
const MANAGED_IMPORT_MAP_PROTOCOL='arcane-test-import-map/1';
|
|
11
|
+
const MANAGED_IMPORT_MAP_BOUNDARIES=new Set(['source','dist','test']);
|
|
12
|
+
|
|
13
|
+
let managedImports=null;
|
|
14
|
+
let managedUrlImports=null;
|
|
15
|
+
|
|
16
|
+
function loaderFailure(message,code='ARCANE_IMPORT_MAP_INVALID'){
|
|
17
|
+
const error=new Error(message);
|
|
18
|
+
error.code=code;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function urlLikeSpecifier(specifier){
|
|
23
|
+
return specifier.startsWith('./')||specifier.startsWith('../')
|
|
24
|
+
||specifier.startsWith('/')||/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(specifier);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function safeManagedTarget(target){
|
|
28
|
+
return typeof target==='string'
|
|
29
|
+
&&target.startsWith('./')
|
|
30
|
+
&&target.length>2
|
|
31
|
+
&&!/[\\%?#\u0000-\u001f\u007f]/u.test(target)
|
|
32
|
+
&&target.slice(2).split('/').every(
|
|
33
|
+
segment=>segment!==''&&segment!=='.'&&segment!=='..'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function samePath(left,right){
|
|
38
|
+
const a=path.resolve(left);
|
|
39
|
+
const b=path.resolve(right);
|
|
40
|
+
return process.platform==='win32'?a.toLowerCase()===b.toLowerCase():a===b;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function pathInside(root,candidate){
|
|
44
|
+
const relative=path.relative(root,candidate);
|
|
45
|
+
return relative===''||(!path.isAbsolute(relative)&&relative!=='..'
|
|
46
|
+
&&!relative.startsWith(`..${path.sep}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function physicalDirectory(baseURL){
|
|
50
|
+
let parsed;
|
|
51
|
+
try{parsed=new URL(baseURL);}
|
|
52
|
+
catch{loaderFailure('Managed test import-map base URL is invalid.');}
|
|
53
|
+
if(parsed.protocol!=='file:'||parsed.username||parsed.password||parsed.search||parsed.hash
|
|
54
|
+
||!parsed.pathname.endsWith('/')){
|
|
55
|
+
loaderFailure('Managed test import-map base URL must select one application directory.');
|
|
56
|
+
}
|
|
57
|
+
const root=fileURLToPath(parsed);
|
|
58
|
+
let info;
|
|
59
|
+
let canonical;
|
|
60
|
+
try{
|
|
61
|
+
info=lstatSync(root);
|
|
62
|
+
canonical=realpathSync(root);
|
|
63
|
+
}catch(error){
|
|
64
|
+
loaderFailure(`Managed test import-map application directory is unavailable: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
if(info.isSymbolicLink()||!info.isDirectory()||!samePath(root,canonical)){
|
|
67
|
+
loaderFailure('Managed test import-map base must be one real application directory.');
|
|
68
|
+
}
|
|
69
|
+
return {url:parsed,root:canonical};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function physicalManagedTarget(target,specifier,boundary,boundaryName){
|
|
73
|
+
const relative=target.slice(2);
|
|
74
|
+
if(boundaryName==='source'&&/^(?:dist|test)\//u.test(relative)){
|
|
75
|
+
loaderFailure(
|
|
76
|
+
`Managed source import-map target selects a different application boundary: ${specifier}.`,
|
|
77
|
+
'ARCANE_IMPORT_MAP_UNRESOLVED'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
let targetURL;
|
|
81
|
+
try{targetURL=new URL(target,boundary.url);}
|
|
82
|
+
catch{loaderFailure(`Managed test import-map target is invalid: ${specifier}.`);}
|
|
83
|
+
if(targetURL.protocol!=='file:'||targetURL.username||targetURL.password
|
|
84
|
+
||targetURL.search||targetURL.hash){
|
|
85
|
+
loaderFailure(`Managed test import-map target is invalid: ${specifier}.`);
|
|
86
|
+
}
|
|
87
|
+
const targetPath=fileURLToPath(targetURL);
|
|
88
|
+
let info;
|
|
89
|
+
let canonical;
|
|
90
|
+
try{
|
|
91
|
+
info=lstatSync(targetPath);
|
|
92
|
+
canonical=realpathSync(targetPath);
|
|
93
|
+
}catch(error){
|
|
94
|
+
loaderFailure(
|
|
95
|
+
`Managed test import-map target is unavailable for ${specifier}: ${error.message}`,
|
|
96
|
+
'ARCANE_IMPORT_MAP_UNRESOLVED'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if(info.isSymbolicLink()||!info.isFile()||!samePath(targetPath,canonical)
|
|
100
|
+
||!pathInside(boundary.root,canonical)){
|
|
101
|
+
loaderFailure(
|
|
102
|
+
`Managed test import-map target leaves its selected application directory: ${specifier}.`,
|
|
103
|
+
'ARCANE_IMPORT_MAP_UNRESOLVED'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return targetURL.href;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function initialize({managedImportMap}={}){
|
|
110
|
+
if(managedImportMap==null){
|
|
111
|
+
managedImports=null;
|
|
112
|
+
managedUrlImports=null;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const imports=managedImportMap.imports;
|
|
116
|
+
if(managedImportMap.protocol!==MANAGED_IMPORT_MAP_PROTOCOL
|
|
117
|
+
||!MANAGED_IMPORT_MAP_BOUNDARIES.has(managedImportMap.boundary)
|
|
118
|
+
||imports===null||typeof imports!=='object'||Array.isArray(imports)){
|
|
119
|
+
loaderFailure('Managed test import-map loader data is malformed.');
|
|
120
|
+
}
|
|
121
|
+
const boundary=physicalDirectory(managedImportMap.baseURL);
|
|
122
|
+
const exact=new Map();
|
|
123
|
+
const urls=new Map();
|
|
124
|
+
for(const [specifier,target] of Object.entries(imports)){
|
|
125
|
+
if(typeof specifier!=='string'||specifier===''||!safeManagedTarget(target)){
|
|
126
|
+
loaderFailure(`Managed test import-map entry is invalid: ${String(specifier)}.`);
|
|
127
|
+
}
|
|
128
|
+
const targetURL=physicalManagedTarget(
|
|
129
|
+
target,
|
|
130
|
+
specifier,
|
|
131
|
+
boundary,
|
|
132
|
+
managedImportMap.boundary
|
|
133
|
+
);
|
|
134
|
+
exact.set(specifier,targetURL);
|
|
135
|
+
if(urlLikeSpecifier(specifier)){
|
|
136
|
+
let keyURL;
|
|
137
|
+
try{keyURL=new URL(specifier,boundary.url).href;}
|
|
138
|
+
catch{loaderFailure(`Managed test import-map URL key is invalid: ${specifier}.`);}
|
|
139
|
+
if(urls.has(keyURL)&&urls.get(keyURL)!==targetURL){
|
|
140
|
+
loaderFailure(`Managed test import-map URL keys collide: ${specifier}.`);
|
|
141
|
+
}
|
|
142
|
+
urls.set(keyURL,targetURL);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
managedImports=exact;
|
|
146
|
+
managedUrlImports=urls;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function managedResolution(specifier,context){
|
|
150
|
+
if(managedImports===null)return null;
|
|
151
|
+
if(managedImports.has(specifier))return managedImports.get(specifier);
|
|
152
|
+
if(urlLikeSpecifier(specifier)&&typeof context.parentURL==='string'){
|
|
153
|
+
let requested;
|
|
154
|
+
try{requested=new URL(specifier,context.parentURL).href;}
|
|
155
|
+
catch{requested=null;}
|
|
156
|
+
if(requested!==null&&managedUrlImports.has(requested)){
|
|
157
|
+
return managedUrlImports.get(requested);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if(specifier.startsWith('arcane/')||specifier.startsWith('#arcane/')){
|
|
161
|
+
loaderFailure(
|
|
162
|
+
`Managed test import map does not expose ${specifier}.`,
|
|
163
|
+
'ARCANE_IMPORT_MAP_UNRESOLVED'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
6
168
|
|
|
7
169
|
export function resolve(specifier,context,nextResolve){
|
|
8
170
|
if(specifier===TESTING_SPECIFIER){
|
|
9
171
|
return {url:TESTING_URL,shortCircuit:true};
|
|
10
172
|
}
|
|
173
|
+
const managed=managedResolution(specifier,context);
|
|
174
|
+
if(managed!==null)return {url:managed,shortCircuit:true};
|
|
11
175
|
if(specifier===RUNTIME_STRONG_TYPE_SPECIFIER
|
|
12
176
|
&&context.parentURL?.startsWith(RUNTIME_ARCANE_URL)){
|
|
13
177
|
return {url:STRONG_TYPE_URL,shortCircuit:true};
|
package/src/testing.mjs
CHANGED
|
@@ -52,7 +52,7 @@ function definition(name,optionsOrCallback,maybeCallback){
|
|
|
52
52
|
if(typeof callback!=='function'){
|
|
53
53
|
throw new TypeError(`Test "${name}" requires a callback function.`);
|
|
54
54
|
}
|
|
55
|
-
return
|
|
55
|
+
return {name,timeoutMs:testTimeout(options),callback};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function test(name,optionsOrCallback,maybeCallback){
|