arcane-os 0.3.1 → 0.3.3
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 +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- 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 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- 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 +113 -69
- 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 +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- 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 +19 -149
- 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 +42 -327
- 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 +48 -66
- 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 +492 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +48 -79
- 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 +55 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +133 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +109 -1558
- 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 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- 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 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {SDK_NAME,SDK_VERSION} from '../constants.mjs';
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
ARCANE_PROTOCOL,
|
|
3
|
+
CLI_EVENT_PROTOCOL,
|
|
4
|
+
SDK_NAME,
|
|
5
|
+
SDK_VERSION,
|
|
6
|
+
TARGET_ADAPTER_PROTOCOL
|
|
7
|
+
} from '../constants.mjs';
|
|
6
8
|
|
|
7
9
|
const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
8
10
|
const LOCAL_TARBALL_PATTERN=/^file:.+\.tgz$/iu;
|
|
@@ -26,11 +28,35 @@ function titleCase(appId){
|
|
|
26
28
|
.join(' ');
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
export function createWorkspaceLockDocument({
|
|
32
|
+
dependencyName=SDK_NAME,
|
|
33
|
+
packageName=SDK_NAME,
|
|
34
|
+
packageVersion=SDK_VERSION,
|
|
35
|
+
packageSource=`node_modules/${dependencyName}`
|
|
36
|
+
}={}){
|
|
37
|
+
if(packageName!==SDK_NAME||typeof packageVersion!=='string'
|
|
38
|
+
||!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/u.test(packageVersion)
|
|
39
|
+
||typeof dependencyName!=='string'||dependencyName.length>214
|
|
40
|
+
||!NPM_PACKAGE_NAME_PATTERN.test(dependencyName)
|
|
41
|
+
||packageSource!==`node_modules/${dependencyName}`){
|
|
42
|
+
throw new Error('Invalid workspace SDK lock authority.');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
schemaVersion:1,
|
|
46
|
+
sdk:{name:packageName,version:packageVersion},
|
|
47
|
+
runtime:{root:`${packageSource}/runtime`},
|
|
48
|
+
sdkBrowserRuntime:{root:`${packageSource}/browser-runtime`},
|
|
49
|
+
protocols:{
|
|
50
|
+
arcane:ARCANE_PROTOCOL,
|
|
51
|
+
cliEvents:CLI_EVENT_PROTOCOL,
|
|
52
|
+
targetAdapter:TARGET_ADAPTER_PROTOCOL
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
29
57
|
export function workspaceTemplate({
|
|
30
58
|
appId,
|
|
31
59
|
displayName,
|
|
32
|
-
runtimeRelease,
|
|
33
|
-
sdkBrowserRuntimeRelease,
|
|
34
60
|
sdkDependencyName=SDK_NAME,
|
|
35
61
|
sdkDependencySpecifier=SDK_VERSION,
|
|
36
62
|
sdkPackageSource=`node_modules/${sdkDependencyName}`,
|
|
@@ -65,31 +91,6 @@ export function workspaceTemplate({
|
|
|
65
91
|
}
|
|
66
92
|
const name=displayName||`Arcane ${titleCase(appId)}`;
|
|
67
93
|
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
94
|
const packageInclude=[`${appId}.css`,'index.html'];
|
|
94
95
|
if(native)packageInclude.push('img/icon.png');
|
|
95
96
|
packageInclude.push('manifest.json','modules');
|
|
@@ -114,34 +115,6 @@ the SDK's license terms; replace it with your own raster application icon when
|
|
|
114
115
|
appropriate.
|
|
115
116
|
`:'';
|
|
116
117
|
|
|
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
118
|
const importMapMarkup=namedImports?` <script type="importmap" data-arcane-import-map>
|
|
146
119
|
{
|
|
147
120
|
"imports": {}
|
|
@@ -176,28 +149,29 @@ appropriate.
|
|
|
176
149
|
- Keep \`arcane/css/theme.css\` before app styles and import \`arcane/ThemeBootstrap\` before app code runs.
|
|
177
150
|
- Use \`rgb(...)\` or \`rgba(...)\` for new CSS colors.
|
|
178
151
|
- Build one named app and one explicit target at a time. Native targets may be unavailable until their adapters are installed.
|
|
152
|
+
- Preserve complete application, model, document, message, log, diagnostic, process, and tool content. Do not truncate, clip, tail, elide, or silently discard it.
|
|
153
|
+
- Do not make ordinary application behavior depend on byte counts, byte limits, byte identities, hashes, digests, or byte-based admission.
|
|
154
|
+
- Optional hardening must remain inactive unless the user expressly selects secure: true for the exact operation. The ordinary path must remain fully functional.
|
|
179
155
|
- Run tests and checks only when the user explicitly selects verification or a distribution artifact requires it.
|
|
180
156
|
`);
|
|
181
157
|
files.set('README.md',`# ${name}
|
|
182
158
|
|
|
183
|
-
This repository contains the portable Arcane application \`${appId}\`. It
|
|
159
|
+
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
160
|
|
|
185
161
|
## Start
|
|
186
162
|
|
|
187
163
|
\`\`\`sh
|
|
188
164
|
npm install
|
|
189
|
-
npm run doctor
|
|
190
165
|
npm run dev
|
|
191
166
|
\`\`\`
|
|
192
167
|
|
|
193
|
-
Open the loopback URL printed by the development server. The source server exposes this app and its
|
|
168
|
+
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
169
|
|
|
195
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.
|
|
196
171
|
|
|
197
|
-
##
|
|
172
|
+
## Optional browser release commands
|
|
198
173
|
|
|
199
174
|
\`\`\`sh
|
|
200
|
-
npm run check
|
|
201
175
|
npm run import-map
|
|
202
176
|
npm run package
|
|
203
177
|
npm run verify
|
|
@@ -207,12 +181,16 @@ npm run run
|
|
|
207
181
|
|
|
208
182
|
The explicit \`import-map\` command refreshes
|
|
209
183
|
\`apps/${appId}/modules/arcane.importmap.json\` and the managed inline browser
|
|
210
|
-
import map in
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
184
|
+
import map in every directly navigable descriptor-admitted \`.html\`/\`.htm\`
|
|
185
|
+
document. HTML component fragments remain package files but do not receive a
|
|
186
|
+
document-level base or managed import map.
|
|
187
|
+
Development, package, and build refresh that shared inventory when the selected operation needs it.
|
|
188
|
+
Named \`arcane/*\` imports resolve against the physical workspace \`arcane/\`
|
|
189
|
+
tree. Packaging copies the complete selected application, runtime, and specifier
|
|
190
|
+
map to \`dist/${appId}\` without running application tests. Run \`verify\` only when
|
|
191
|
+
the user explicitly selects verification or a release artifact that requires it;
|
|
192
|
+
\`bundle\` creates the distributable archive and \`run\` launches the selected
|
|
193
|
+
packaged browser release.
|
|
216
194
|
|
|
217
195
|
Native targets are provider-supplied and must be scaffolded and selected
|
|
218
196
|
explicitly; this browser workflow does not imply a standalone native executable.
|
|
@@ -254,7 +232,7 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
254
232
|
{
|
|
255
233
|
source:'arcane',
|
|
256
234
|
destination:'arcane',
|
|
257
|
-
include:['components','css','dependencies','entities','img','modules','sdk'
|
|
235
|
+
include:['components','css','dependencies','entities','img','modules','sdk'],
|
|
258
236
|
exclude:[]
|
|
259
237
|
},
|
|
260
238
|
{
|
|
@@ -266,54 +244,10 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
266
244
|
]
|
|
267
245
|
}
|
|
268
246
|
}));
|
|
269
|
-
files.set('arcane.lock.json',json({
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
`);
|
|
247
|
+
files.set('arcane.lock.json',json(createWorkspaceLockDocument({
|
|
248
|
+
dependencyName:sdkDependencyName,
|
|
249
|
+
packageSource:sdkPackageSource
|
|
250
|
+
})));
|
|
317
251
|
files.set(`apps/${appId}/arcane-app.json`,json({
|
|
318
252
|
schemaVersion:2,
|
|
319
253
|
id:appId,
|
|
@@ -331,15 +265,6 @@ jobs:
|
|
|
331
265
|
exclude:[],
|
|
332
266
|
shared:['browser-runtime']
|
|
333
267
|
},
|
|
334
|
-
permissions:{
|
|
335
|
-
capabilities:[],
|
|
336
|
-
methods:[]
|
|
337
|
-
},
|
|
338
|
-
security:{
|
|
339
|
-
connectOrigins:[],
|
|
340
|
-
frameOrigins:[],
|
|
341
|
-
mediaOrigins:[]
|
|
342
|
-
},
|
|
343
268
|
native:{
|
|
344
269
|
type:'app',
|
|
345
270
|
icon:native?'img/icon.png':null,
|
|
@@ -348,7 +273,7 @@ jobs:
|
|
|
348
273
|
},
|
|
349
274
|
requirements:{
|
|
350
275
|
arcaneProtocol:'arcane/1',
|
|
351
|
-
minimumCoreVersion,
|
|
276
|
+
...(native?{minimumCoreVersion}:{}),
|
|
352
277
|
features:[]
|
|
353
278
|
},
|
|
354
279
|
targets:native?['browser',target].sort():['browser']
|
|
@@ -360,18 +285,13 @@ jobs:
|
|
|
360
285
|
version:'0.1.0',
|
|
361
286
|
entry:'index.html',
|
|
362
287
|
strategy:'static',
|
|
363
|
-
security:{
|
|
364
|
-
connectOrigins:[],
|
|
365
|
-
frameOrigins:[],
|
|
366
|
-
mediaOrigins:[]
|
|
367
|
-
},
|
|
368
288
|
include:packageInclude,
|
|
369
289
|
exclude:[],
|
|
370
290
|
shared:['browser-runtime']
|
|
371
291
|
}));
|
|
372
292
|
files.set(`apps/${appId}/manifest.json`,json({
|
|
373
293
|
name,
|
|
374
|
-
short_name:titleCase(appId)
|
|
294
|
+
short_name:titleCase(appId),
|
|
375
295
|
start_url:'./index.html',
|
|
376
296
|
display:'standalone',
|
|
377
297
|
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){
|