arcane-os 0.5.16 → 0.5.18
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 +44 -0
- package/README.md +42 -19
- package/bin/arcane-test.mjs +16 -13
- package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
- package/browser-runtime/ai/browser-speech-providers.mjs +53 -46
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
- package/browser-runtime/ai/browser-wasm.mjs +6 -3
- package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
- package/browser-runtime/ai/model-controller.mjs +94 -91
- package/browser-runtime/ai/speech-worker-client.mjs +12 -9
- package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
- package/browser-runtime/dependencies/strong-type/index.js +207 -82
- package/browser-runtime/dependencies/strong-type/package.json +15 -6
- package/browser-runtime/dom-event-instrumentation.mjs +24 -20
- package/browser-runtime/event-manager.mjs +111 -108
- package/browser-runtime/speech-text.mjs +35 -0
- package/docs/architecture.md +1 -1
- package/docs/reference/README.md +8 -8
- package/docs/reference/ai/browser-speech.md +230 -16
- package/docs/reference/ai/twin-cloud.md +1 -1
- package/docs/reference/availability-and-normalization.md +42 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/inventory/package-api.json +60 -4
- package/docs/reference/inventory/runtime-components.json +2 -2
- package/docs/reference/inventory/runtime-modules.json +29 -31
- package/docs/reference/protocols.md +17 -13
- package/docs/reference/runtime-components.md +6 -5
- package/docs/reference/runtime-modules.md +212 -70
- package/docs/reference/sdk-api.md +197 -11
- package/examples/wasm-ai-demo/index.html +1 -0
- package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
- package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
- package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
- package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
- package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
- package/node_modules/strong-type/README.md +42 -24
- package/node_modules/strong-type/benchmark/core.js +324 -0
- package/node_modules/strong-type/index.js +207 -82
- package/node_modules/strong-type/package.json +14 -6
- package/package.json +4 -3
- package/runtime/arcane/components/app-bar.html +5 -2
- package/runtime/arcane/components/assistant-panel.html +5 -2
- package/runtime/arcane/components/chart.html +15 -12
- package/runtime/arcane/components/chat.html +118 -141
- package/runtime/arcane/components/dashboard-config.html +6 -3
- package/runtime/arcane/components/data-view.html +4 -1
- package/runtime/arcane/components/directory-picker.html +5 -2
- package/runtime/arcane/components/document-inspector.html +6 -3
- package/runtime/arcane/components/file-drop.html +5 -2
- package/runtime/arcane/components/file-inspector.html +9 -6
- package/runtime/arcane/components/file-manager.html +16 -13
- package/runtime/arcane/components/local-ai-status.html +5 -2
- package/runtime/arcane/components/markdown-document.html +14 -11
- package/runtime/arcane/components/markdown-editor.html +4 -1
- package/runtime/arcane/components/modal.html +4 -1
- package/runtime/arcane/components/output-panel.html +6 -3
- package/runtime/arcane/components/preferences-form.html +4 -1
- package/runtime/arcane/components/record-timeline.html +8 -5
- package/runtime/arcane/components/relationship-board.html +9 -6
- package/runtime/arcane/components/source-code-viewer.html +12 -9
- package/runtime/arcane/components/source-explanation.html +8 -5
- package/runtime/arcane/components/speech.html +24 -21
- package/runtime/arcane/components/summary-strip.html +4 -1
- package/runtime/arcane/components/task-progress.html +6 -3
- package/runtime/arcane/components/terminal-workspace.html +4 -1
- package/runtime/arcane/components/voice-transcription.html +11 -8
- package/runtime/arcane/components/web-navigator.html +5 -2
- package/runtime/arcane/entities/ApiModelRecord.js +5 -2
- package/runtime/arcane/entities/Calculation.js +5 -2
- package/runtime/arcane/entities/Chat.js +33 -33
- package/runtime/arcane/entities/CommunicationMessage.js +4 -1
- package/runtime/arcane/entities/CommunicationThread.js +4 -1
- package/runtime/arcane/entities/File.js +1 -1
- package/runtime/arcane/entities/Image.js +8 -5
- package/runtime/arcane/entities/IntentEnvelope.js +20 -17
- package/runtime/arcane/entities/Preference.js +9 -6
- package/runtime/arcane/entities/Theme.js +5 -2
- package/runtime/arcane/entities/User.js +11 -11
- package/runtime/arcane/entities/Weather.js +5 -2
- package/runtime/arcane/modules/AI.js +503 -187
- package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
- package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
- package/runtime/arcane/modules/AIProviderRuntime.js +117 -92
- package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
- package/runtime/arcane/modules/AIRuntimeState.js +29 -26
- package/runtime/arcane/modules/AnsiText.js +4 -1
- package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
- package/runtime/arcane/modules/AppDataScope.js +8 -5
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
- package/runtime/arcane/modules/AsyncBoundary.js +13 -10
- package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
- package/runtime/arcane/modules/CalculatorEngine.js +5 -2
- package/runtime/arcane/modules/ChatRecords.js +18 -15
- package/runtime/arcane/modules/CommunicationAppController.js +9 -6
- package/runtime/arcane/modules/CommunicationHub.js +9 -6
- package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
- package/runtime/arcane/modules/ComponentContracts.js +56 -53
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
- package/runtime/arcane/modules/ConversationActionItems.js +15 -12
- package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
- package/runtime/arcane/modules/ConversationTimebox.js +28 -25
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
- package/runtime/arcane/modules/DBLS.js +7 -4
- package/runtime/arcane/modules/DBOPFS.js +7 -7
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
- package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
- package/runtime/arcane/modules/DirectoryPicker.js +8 -5
- package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
- package/runtime/arcane/modules/DocumentNavigation.js +7 -4
- package/runtime/arcane/modules/Errors.js +28 -25
- package/runtime/arcane/modules/HTMLImport.js +7 -4
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
- package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
- package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
- package/runtime/arcane/modules/MD.js +1 -1
- package/runtime/arcane/modules/Mail.js +46 -43
- package/runtime/arcane/modules/MailOutbox.mjs +48 -45
- package/runtime/arcane/modules/MailTransport.mjs +29 -26
- package/runtime/arcane/modules/MarkdownSpeech.js +1 -50
- package/runtime/arcane/modules/MemoryRecords.js +7 -4
- package/runtime/arcane/modules/MessageAdvisory.js +6 -3
- package/runtime/arcane/modules/ModelDefinition.js +7 -4
- package/runtime/arcane/modules/Ollama.js +6 -3
- package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
- package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
- package/runtime/arcane/modules/PreferenceStore.js +20 -17
- package/runtime/arcane/modules/PreparedSpeech.js +485 -0
- package/runtime/arcane/modules/Questionnaire.js +6 -3
- package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
- package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
- package/runtime/arcane/modules/RecordReviewStore.js +12 -9
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
- package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
- package/runtime/arcane/modules/ScreenCapture.js +20 -17
- package/runtime/arcane/modules/SpeechPlayback.js +61 -28
- package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
- package/runtime/arcane/modules/SystemAppearance.js +5 -2
- package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
- package/runtime/arcane/modules/TerminalClient.js +14 -11
- package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
- package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
- package/runtime/arcane/modules/ThemeManager.js +5 -2
- package/runtime/arcane/modules/TimeGuard.js +1 -1
- package/runtime/arcane/modules/ToolCallRouter.js +11 -8
- package/runtime/arcane/modules/WaitForComponent.js +19 -16
- package/runtime/arcane/modules/YouTubeMedia.js +4 -1
- package/runtime/strong-type/index.js +1276 -352
- package/runtime/strong-type/package.json +69 -45
- package/src/app-descriptor.mjs +17 -14
- package/src/application-tests.mjs +4 -1
- package/src/cli/main.mjs +8 -5
- package/src/constants.mjs +4 -1
- package/src/dev-server.mjs +8 -5
- package/src/doctor.mjs +5 -2
- package/src/dom-event-instrumentation.mjs +24 -20
- package/src/errors.mjs +7 -3
- package/src/event-manager.mjs +111 -108
- package/src/event-queue.mjs +8 -5
- package/src/events.mjs +12 -9
- package/src/import-map.mjs +36 -27
- package/src/installed-sdk-runtime.mjs +4 -1
- package/src/integrated-provider-loader.mjs +9 -6
- package/src/mail-credentials.mjs +16 -13
- package/src/mail-server.mjs +53 -50
- package/src/mail.mjs +18 -15
- package/src/native-plan.mjs +12 -9
- package/src/native-provider-loader.mjs +7 -4
- package/src/packager/core.mjs +18 -15
- package/src/process.mjs +6 -3
- package/src/release-bundle.mjs +14 -11
- package/src/runtime.mjs +4 -1
- package/src/scaffold.mjs +9 -6
- package/src/sdk-browser-runtime.mjs +4 -1
- package/src/source-server.mjs +17 -14
- package/src/targets/index.mjs +5 -2
- package/src/templates/workspace-template.mjs +13 -6
- package/src/testing-loader.mjs +7 -4
- package/src/testing.mjs +10 -7
- package/src/toolchain.mjs +13 -10
- package/src/update-check.mjs +9 -6
- package/src/workspace-operation-lock.mjs +14 -11
- package/src/workspace-runtime.mjs +4 -1
- package/src/workspace.mjs +17 -14
- package/runtime/arcane/modules/AIResponseLength.js +0 -32
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Brandon Nozaki Miller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import Is from './index.js';
|
|
2
|
+
import * as util from 'node:util';
|
|
3
|
+
import * as stream from 'node:stream';
|
|
4
|
+
import * as events from 'node:events';
|
|
5
|
+
import * as crypto from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
let timeoutConstructor;
|
|
8
|
+
let immediateConstructor;
|
|
9
|
+
|
|
10
|
+
const instanceOf=function(value,constructor){
|
|
11
|
+
if(typeof constructor !== 'function'){
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try{
|
|
16
|
+
return value instanceof constructor;
|
|
17
|
+
}catch(err){
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const timerConstructor=function(type){
|
|
23
|
+
if(type === 'timeout' && !timeoutConstructor){
|
|
24
|
+
const timeout=setTimeout(()=>{},0);
|
|
25
|
+
clearTimeout(timeout);
|
|
26
|
+
timeoutConstructor=timeout.constructor;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if(type === 'immediate' && !immediateConstructor && typeof setImmediate === 'function'){
|
|
30
|
+
const immediate=setImmediate(()=>{});
|
|
31
|
+
clearImmediate(immediate);
|
|
32
|
+
immediateConstructor=immediate.constructor;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return type === 'timeout' ? timeoutConstructor : immediateConstructor;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class IsNode extends Is{
|
|
39
|
+
utilTypeCheck(value,method,typeName){
|
|
40
|
+
const check=util.types && util.types[method];
|
|
41
|
+
let pass=false;
|
|
42
|
+
try{
|
|
43
|
+
pass=typeof check === 'function' && check(value);
|
|
44
|
+
}catch(err){
|
|
45
|
+
pass=false;
|
|
46
|
+
}
|
|
47
|
+
return this.check(value,pass,typeName);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
buffer(value){
|
|
51
|
+
return this.check(value,Buffer.isBuffer(value),'Buffer');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
nodeStream(value){
|
|
55
|
+
return this.check(value,instanceOf(value,stream.Stream),'Node Stream');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
nodeReadable(value){
|
|
59
|
+
return this.check(value,instanceOf(value,stream.Readable),'Node Readable');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
nodeWritable(value){
|
|
63
|
+
return this.check(value,instanceOf(value,stream.Writable),'Node Writable');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
nodeDuplex(value){
|
|
67
|
+
return this.check(value,instanceOf(value,stream.Duplex),'Node Duplex');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
nodeTransform(value){
|
|
71
|
+
return this.check(value,instanceOf(value,stream.Transform),'Node Transform');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
nodePassThrough(value){
|
|
75
|
+
return this.check(value,instanceOf(value,stream.PassThrough),'Node PassThrough');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
eventEmitter(value){
|
|
79
|
+
return this.check(value,instanceOf(value,events.EventEmitter),'EventEmitter');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
timeout(value){
|
|
83
|
+
const constructor=timerConstructor('timeout');
|
|
84
|
+
return this.check(value,instanceOf(value,constructor),'Timeout');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
immediate(value){
|
|
88
|
+
const constructor=timerConstructor('immediate');
|
|
89
|
+
return this.check(value,instanceOf(value,constructor),'Immediate');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
keyObject(value){
|
|
93
|
+
return this.check(value,instanceOf(value,crypto.KeyObject),'KeyObject');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
x509Certificate(value){
|
|
97
|
+
return this.check(value,instanceOf(value,crypto.X509Certificate),'X509Certificate');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
proxy(value){
|
|
101
|
+
return this.utilTypeCheck(value,'isProxy','Proxy');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
moduleNamespaceObject(value){
|
|
105
|
+
return this.utilTypeCheck(value,'isModuleNamespaceObject','module namespace object');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
external(value){
|
|
109
|
+
return this.utilTypeCheck(value,'isExternal','native external value');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
nativeError(value){
|
|
113
|
+
return this.utilTypeCheck(value,'isNativeError','native Error');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
mapIterator(value){
|
|
117
|
+
return this.utilTypeCheck(value,'isMapIterator','Map iterator');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setIterator(value){
|
|
121
|
+
return this.utilTypeCheck(value,'isSetIterator','Set iterator');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {IsNode as default,IsNode};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "strong-type",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Dependency-free native ESM type enforcement for JavaScript values, objects, classes, browsers, and Node.",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./index.js": "./index.js",
|
|
10
|
+
"./node": "./node.js",
|
|
11
|
+
"./node.js": "./node.js",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"index.js",
|
|
16
|
+
"node.js",
|
|
17
|
+
"assets/strong-type-header.png",
|
|
18
|
+
"README.md",
|
|
19
|
+
"licence"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"directories": {
|
|
23
|
+
"example": "example"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=12.21.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "node scripts/test.js",
|
|
30
|
+
"test:core": "node scripts/test.js core",
|
|
31
|
+
"test:node": "node scripts/test.js node",
|
|
32
|
+
"test:docs": "node scripts/test.js docs",
|
|
33
|
+
"test:legacy": "node scripts/test.js legacy",
|
|
34
|
+
"coverage": "node --test --experimental-test-coverage --test-coverage-include=index.js --test-coverage-include=node.js --test-coverage-lines=95 --test-coverage-branches=90 --test-coverage-functions=95 test/CI.js test/node.js test/docs.js",
|
|
35
|
+
"start": "node scripts/serve.js",
|
|
36
|
+
"nodeExample": "node ./example/node/typecheck.js",
|
|
37
|
+
"check": "npm test"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/RIAEvangelist/strong-type.git"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"strong",
|
|
45
|
+
"type",
|
|
46
|
+
"validation",
|
|
47
|
+
"validate",
|
|
48
|
+
"check",
|
|
49
|
+
"checking",
|
|
50
|
+
"esm",
|
|
51
|
+
"isomorphic",
|
|
52
|
+
"browser",
|
|
53
|
+
"node"
|
|
54
|
+
],
|
|
55
|
+
"author": "Roshi _ _",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/RIAEvangelist/strong-type/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://riaevangelist.github.io/strong-type/"
|
|
61
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[Overview](https://riaevangelist.github.io/strong-type/) · [Validator reference](https://riaevangelist.github.io/strong-type/reference.html) · [Tests & coverage](https://riaevangelist.github.io/strong-type/testing.html) · [Playground](https://riaevangelist.github.io/strong-type/playground.html)
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/strong-type) [](https://www.npmjs.com/package/strong-type) [](https://github.com/RIAEvangelist/strong-type/actions/workflows/ci.yml) [](./licence) [](https://www.npmjs.com/package/strong-type) [](https://www.npmjs.com/package/strong-type) [](https://github.com/RIAEvangelist/strong-type/actions/workflows/ci.yml) [](./licence) [](./package.json)
|
|
8
8
|
|
|
9
9
|
Native type enforcement for JavaScript. `strong-type` runs as the same untransformed ES module in browsers and Node. Strict checks throw a useful `TypeError`; non-strict checks return a boolean.
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ Native type enforcement for JavaScript. `strong-type` runs as the same untransfo
|
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| Module format | Native ESM | The checked-in JavaScript is what the runtime executes. |
|
|
16
16
|
| Runtime dependencies | None | No third-party production packages. |
|
|
17
|
-
| Development dependencies |
|
|
17
|
+
| Development dependencies | `vanilla-test` 2.1.0 | The exact test-runner pin is development-only; runtime installs remain dependency-free. |
|
|
18
18
|
| Bundler | Not required | Browser and Node imports work directly. |
|
|
19
19
|
| Transpiler | Not required | No generated CommonJS or compatibility copy. |
|
|
20
20
|
| Default entry point | Isomorphic | `index.js` contains no `node:*` imports. |
|
|
@@ -361,45 +361,63 @@ Version 2 removes several coercive edge cases while retaining the original metho
|
|
|
361
361
|
| `union(value,'toString')` | Could call an inherited method | Rejected |
|
|
362
362
|
| Subclass validators in `union` | Lost by constructing base `Is` | Preserved |
|
|
363
363
|
|
|
364
|
+
## Core benchmarks
|
|
365
|
+
|
|
366
|
+
The dependency-free benchmark runner measures representative successful and rejecting core validator paths with rotating fixtures, calibrated samples, result verification, dedicated per-case loops, median timings, and median absolute deviation.
|
|
367
|
+
|
|
368
|
+
```sh
|
|
369
|
+
npm run benchmark
|
|
370
|
+
npm run benchmark:baseline
|
|
371
|
+
npm run benchmark:historical
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The default command measures the current checkout. `benchmark:baseline` compares it with the pre-optimization `700059a` implementation, while `benchmark:historical` provides a same-realm throughput reference against 1.1.0. Both comparison commands require a Git checkout containing the referenced commits. Absolute timings are harness-specific JIT throughput references, and historical results are informational because 1.1.0 did not provide the current cross-realm and spoof-resistance guarantees.
|
|
375
|
+
|
|
364
376
|
## Tests and coverage
|
|
365
377
|
|
|
366
|
-
|
|
378
|
+
One canonical registry runs through `vanilla-test` 2.1.0 on Node 22.12 or newer, with every expectation assigned once to Unit, Functional, Integration, or Regression. CI exercises both the minimum VanillaTest runtime and Node 24. Older supported Node releases run that same registry through the small dependency-free compatibility adapter, so the development runner does not change the package's runtime support or add production dependencies.
|
|
367
379
|
|
|
368
380
|
| Test suite | Result on Node 24.18.0 | Covers |
|
|
369
381
|
|---|---:|---|
|
|
370
|
-
|
|
|
371
|
-
|
|
|
372
|
-
|
|
|
373
|
-
|
|
|
382
|
+
| Unit | 586 passed · 13 guarded skips | Atomic validator contracts across all 201 core and Node validators, including guarded capabilities |
|
|
383
|
+
| Functional | 15 passed | Strict and non-strict modes, unions, custom validators, and Node adapter inheritance |
|
|
384
|
+
| Integration | 554 passed | Reference and README completeness, site links, package exports, coverage configuration, and CI wiring |
|
|
385
|
+
| Regression | 23 passed | Coercion fixes, cross-realm brands, spoof resistance, revoked proxies, and cleanup resilience |
|
|
386
|
+
| **Total** | **1,178 passed · 0 failed · 13 skipped** | Four exclusive suites from one non-duplicative registry |
|
|
374
387
|
|
|
375
388
|
A guarded skip means the runtime does not expose that host API. It is a capability result, not an ignored failure.
|
|
376
389
|
|
|
377
|
-
Coverage uses
|
|
390
|
+
Coverage uses VanillaTest's native Node V8 collector and includes only the two shipped runtime sources. Its executable-range and block-range metrics come directly from V8 and are not interchangeable with parser-derived statement or branch percentages.
|
|
378
391
|
|
|
379
|
-
| Source |
|
|
380
|
-
|
|
381
|
-
| `index.js` |
|
|
382
|
-
| `node.js` |
|
|
383
|
-
| **Total** | **
|
|
392
|
+
| Source | Executable ranges | Block ranges | Function ranges | Executable lines |
|
|
393
|
+
|---|---:|---:|---:|---:|
|
|
394
|
+
| `index.js` | 89.31% · 301/337 | 68.46% · 76/111 | 99.55% · 225/226 | 90.28% · 920/1019 |
|
|
395
|
+
| `node.js` | 88.23% · 30/34 | 80.00% · 8/10 | 91.66% · 22/24 | 91.91% · 91/99 |
|
|
396
|
+
| **Total** | **89.21% · 331/371** | **69.42% · 84/121** | **98.80% · 247/250** | **90.42% · 1011/1118** |
|
|
384
397
|
|
|
385
|
-
|
|
|
398
|
+
| Per-file gate | Lowest current file | Required |
|
|
386
399
|
|---|---:|---:|
|
|
387
|
-
|
|
|
388
|
-
|
|
|
389
|
-
|
|
|
400
|
+
| Executable ranges | 88.23% | 85% |
|
|
401
|
+
| Block ranges | 68.46% | 65% |
|
|
402
|
+
| Function ranges | 91.66% | 90% |
|
|
403
|
+
| Executable lines | 90.28% | 90% |
|
|
390
404
|
|
|
391
|
-
|
|
405
|
+
Test and documentation files are excluded from the percentages. See the [full test, coverage, and CI explanation](https://riaevangelist.github.io/strong-type/testing.html).
|
|
392
406
|
|
|
393
407
|
## Commands
|
|
394
408
|
|
|
395
409
|
| Command | What it does | Third-party tooling |
|
|
396
410
|
|---|---|---|
|
|
397
|
-
| `npm
|
|
398
|
-
| `npm run
|
|
399
|
-
| `npm run
|
|
400
|
-
| `npm
|
|
401
|
-
| `npm run test:
|
|
402
|
-
| `npm run
|
|
411
|
+
| `npm run benchmark` | Measures current core validator throughput with verified results and dispersion | None |
|
|
412
|
+
| `npm run benchmark:baseline` | Compares current core throughput with pre-optimization commit `700059a` | Git |
|
|
413
|
+
| `npm run benchmark:historical` | Compares overlapping same-realm paths with 1.1.0 commit `3229b47` | Git |
|
|
414
|
+
| `npm test` | Runs Unit, Functional, Integration, and Regression through one registry | `vanilla-test` 2.1.0 |
|
|
415
|
+
| `npm run test:unit` | Runs atomic core and Node validator contracts | `vanilla-test` 2.1.0 |
|
|
416
|
+
| `npm run test:functional` | Runs public mode, union, and adapter workflows | `vanilla-test` 2.1.0 |
|
|
417
|
+
| `npm run test:integration` | Runs documentation, package, site, and tooling boundaries | `vanilla-test` 2.1.0 |
|
|
418
|
+
| `npm run test:regression` | Runs corrected edge cases and hostile-value protections | `vanilla-test` 2.1.0 |
|
|
419
|
+
| `npm run test:legacy` | Runs the same registered cases through the compatibility adapter | None |
|
|
420
|
+
| `npm run coverage` | Runs the canonical registry through VanillaTest's native Node V8 coverage collector | `vanilla-test` 2.1.0 |
|
|
403
421
|
| `npm start` | Serves the docs and playground at `http://localhost:8000/` | None |
|
|
404
422
|
| `npm run nodeExample` | Runs the Node example | None |
|
|
405
423
|
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import {execFileSync} from 'node:child_process';
|
|
2
|
+
import Is from '../index.js';
|
|
3
|
+
|
|
4
|
+
const targetSampleMs=100;
|
|
5
|
+
const sampleCount=7;
|
|
6
|
+
const minimumIterations=1_000;
|
|
7
|
+
const maximumIterations=50_000_000;
|
|
8
|
+
const fixtureCount=64;
|
|
9
|
+
const fixtureMask=fixtureCount-1;
|
|
10
|
+
const historical=process.argv.includes('--historical');
|
|
11
|
+
const baseline=process.argv.includes('--baseline');
|
|
12
|
+
const historicalCommit='3229b47';
|
|
13
|
+
const baselineCommit='700059a';
|
|
14
|
+
let sink=0;
|
|
15
|
+
let compareConversions=0;
|
|
16
|
+
let runnerSequence=0;
|
|
17
|
+
|
|
18
|
+
const median=function(values){
|
|
19
|
+
const sorted=values.slice().sort((left,right)=>left-right);
|
|
20
|
+
return sorted[Math.floor(sorted.length/2)];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const medianAbsoluteDeviation=function(values,center){
|
|
24
|
+
return median(values.map(value=>Math.abs(value-center)));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const runnerFor=function(current){
|
|
28
|
+
// A separately compiled loop keeps V8 feedback local to one case and implementation.
|
|
29
|
+
const name=`strongTypeBenchmark${runnerSequence++}`;
|
|
30
|
+
const create=Function('check','expected','hrtime',`
|
|
31
|
+
return function ${name}(iterations){
|
|
32
|
+
let matches=0;
|
|
33
|
+
const start=hrtime();
|
|
34
|
+
for(let index=0;index<iterations;index++){
|
|
35
|
+
matches+=check(index) === expected ? 1 : 0;
|
|
36
|
+
}
|
|
37
|
+
return [matches,Number(hrtime()-start)];
|
|
38
|
+
};
|
|
39
|
+
`);
|
|
40
|
+
return create(current.check,current.expected,process.hrtime.bigint);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const elapsed=function(current,iterations){
|
|
44
|
+
const [matches,nanoseconds]=current.run(iterations);
|
|
45
|
+
if(matches !== iterations){
|
|
46
|
+
throw new Error(`${current.name} returned an unexpected result after ${matches.toLocaleString('en-US')} of ${iterations.toLocaleString('en-US')} iterations`);
|
|
47
|
+
}
|
|
48
|
+
sink=(sink+matches+iterations)|0;
|
|
49
|
+
return nanoseconds/1e6;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const calibrate=function(current){
|
|
53
|
+
let iterations=20_000;
|
|
54
|
+
let duration=elapsed(current,iterations);
|
|
55
|
+
let warmupDuration=duration;
|
|
56
|
+
while((duration < targetSampleMs/4 || warmupDuration < 50) && iterations < maximumIterations){
|
|
57
|
+
const scale=Math.max(2,Math.min(10,Math.ceil((targetSampleMs/Math.max(duration,0.01))/2)));
|
|
58
|
+
iterations=Math.min(maximumIterations,iterations*scale);
|
|
59
|
+
duration=elapsed(current,iterations);
|
|
60
|
+
warmupDuration+=duration;
|
|
61
|
+
}
|
|
62
|
+
const projected=Math.round(iterations*(targetSampleMs/Math.max(duration,0.01)));
|
|
63
|
+
return Math.max(minimumIterations,Math.min(maximumIterations,projected));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const summarize=function(name,iterations,samples){
|
|
67
|
+
const duration=median(samples);
|
|
68
|
+
const deviation=medianAbsoluteDeviation(samples,duration);
|
|
69
|
+
const nanoseconds=duration*1e6/iterations;
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
iterations,
|
|
73
|
+
nanoseconds,
|
|
74
|
+
operationsPerSecond:1e9/nanoseconds,
|
|
75
|
+
deviationPercent:duration ? deviation/duration*100 : 0
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const benchmark=function(current){
|
|
80
|
+
const iterations=calibrate(current);
|
|
81
|
+
const samples=[];
|
|
82
|
+
for(let sample=0;sample<sampleCount;sample++){
|
|
83
|
+
samples.push(elapsed(current,iterations));
|
|
84
|
+
}
|
|
85
|
+
return summarize(current.name,iterations,samples);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const benchmarkPair=function(current,baseline,baselineFirst){
|
|
89
|
+
let currentIterations;
|
|
90
|
+
let baselineIterations;
|
|
91
|
+
if(baselineFirst){
|
|
92
|
+
baselineIterations=calibrate(baseline);
|
|
93
|
+
currentIterations=calibrate(current);
|
|
94
|
+
}else{
|
|
95
|
+
currentIterations=calibrate(current);
|
|
96
|
+
baselineIterations=calibrate(baseline);
|
|
97
|
+
}
|
|
98
|
+
const currentSamples=[];
|
|
99
|
+
const baselineSamples=[];
|
|
100
|
+
for(let sample=0;sample<sampleCount;sample++){
|
|
101
|
+
const runBaselineFirst=sample%2 === 0 ? baselineFirst : !baselineFirst;
|
|
102
|
+
if(runBaselineFirst){
|
|
103
|
+
baselineSamples.push(elapsed(baseline,baselineIterations));
|
|
104
|
+
currentSamples.push(elapsed(current,currentIterations));
|
|
105
|
+
}else{
|
|
106
|
+
currentSamples.push(elapsed(current,currentIterations));
|
|
107
|
+
baselineSamples.push(elapsed(baseline,baselineIterations));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
current:summarize(current.name,currentIterations,currentSamples),
|
|
112
|
+
baseline:summarize(baseline.name,baselineIterations,baselineSamples)
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const fixtures=function(create){
|
|
117
|
+
return Array.from({length:fixtureCount},(_,index)=>create(index));
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const numbers=fixtures(index=>index+0.25);
|
|
121
|
+
const definedValues=fixtures(index=>index%2 ? index : null);
|
|
122
|
+
const plainObjects=fixtures(index=>({index}));
|
|
123
|
+
const arrays=fixtures(index=>[index]);
|
|
124
|
+
const nullPrototypeObjects=fixtures(index=>Object.assign(Object.create(null),{index}));
|
|
125
|
+
const dates=fixtures(index=>new Date(1_700_000_000_000+index));
|
|
126
|
+
const maps=fixtures(index=>new Map([['index',index]]));
|
|
127
|
+
const sets=fixtures(index=>new Set([index]));
|
|
128
|
+
const regularExpressions=fixtures(index=>new RegExp(String(index),'u'));
|
|
129
|
+
const arrayBuffers=fixtures(()=>new ArrayBuffer(8));
|
|
130
|
+
const sharedArrayBuffers=typeof SharedArrayBuffer === 'function' ? fixtures(()=>new SharedArrayBuffer(8)) : [];
|
|
131
|
+
const dataViews=arrayBuffers.map(buffer=>new DataView(buffer));
|
|
132
|
+
const uint8Arrays=fixtures(index=>new Uint8Array([index]));
|
|
133
|
+
const boxedValues=fixtures(index=>{
|
|
134
|
+
const type=index%5;
|
|
135
|
+
if(type === 0) return Object(Boolean(index%2));
|
|
136
|
+
if(type === 1) return Object(index);
|
|
137
|
+
if(type === 2) return Object(BigInt(index));
|
|
138
|
+
if(type === 3) return Object(String(index));
|
|
139
|
+
return Object(Symbol(String(index)));
|
|
140
|
+
});
|
|
141
|
+
const comparableValues=fixtures(()=>Object.freeze({
|
|
142
|
+
toString(){
|
|
143
|
+
compareConversions++;
|
|
144
|
+
return 'comparable';
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
const baselineComparableValues=fixtures(()=>Object.freeze({
|
|
148
|
+
toString(){
|
|
149
|
+
return 'comparable';
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
const makeCase=function(name,expected,check){
|
|
154
|
+
const current={name,expected,check};
|
|
155
|
+
current.run=runnerFor(current);
|
|
156
|
+
return current;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const currentCases=function(Class){
|
|
160
|
+
const is=new Class;
|
|
161
|
+
const weakIs=new Class(false);
|
|
162
|
+
const comparisonValues=Class === Is ? comparableValues : baselineComparableValues;
|
|
163
|
+
const cases=[
|
|
164
|
+
makeCase('number/pass',true,index=>weakIs.number(numbers[index&fixtureMask])),
|
|
165
|
+
makeCase('number/fail',false,index=>weakIs.number(plainObjects[index&fixtureMask])),
|
|
166
|
+
makeCase('compare/pass',true,index=>weakIs.compare(comparisonValues[index&fixtureMask],comparisonValues[index&fixtureMask])),
|
|
167
|
+
makeCase('array/pass',true,index=>weakIs.array(arrays[index&fixtureMask])),
|
|
168
|
+
makeCase('array/fail',false,index=>weakIs.array(plainObjects[index&fixtureMask])),
|
|
169
|
+
makeCase('nullPrototypeObject/pass',true,index=>weakIs.nullPrototypeObject(nullPrototypeObjects[index&fixtureMask])),
|
|
170
|
+
makeCase('nullPrototypeObject/fail',false,index=>weakIs.nullPrototypeObject(plainObjects[index&fixtureMask])),
|
|
171
|
+
makeCase('date/pass',true,index=>weakIs.date(dates[index&fixtureMask])),
|
|
172
|
+
makeCase('date/fail',false,index=>weakIs.date(plainObjects[index&fixtureMask])),
|
|
173
|
+
makeCase('validDate/pass',true,index=>weakIs.validDate(dates[index&fixtureMask])),
|
|
174
|
+
makeCase('map/pass',true,index=>weakIs.map(maps[index&fixtureMask])),
|
|
175
|
+
makeCase('map/fail',false,index=>weakIs.map(plainObjects[index&fixtureMask])),
|
|
176
|
+
makeCase('arrayBufferView/pass',true,index=>weakIs.arrayBufferView(uint8Arrays[index&fixtureMask])),
|
|
177
|
+
makeCase('typedArray/pass',true,index=>weakIs.typedArray(uint8Arrays[index&fixtureMask])),
|
|
178
|
+
makeCase('uint8Array/pass',true,index=>weakIs.uint8Array(uint8Arrays[index&fixtureMask])),
|
|
179
|
+
makeCase('uint8Array/reject-data-view',false,index=>weakIs.uint8Array(dataViews[index&fixtureMask])),
|
|
180
|
+
makeCase('boxedPrimitive/pass',true,index=>weakIs.boxedPrimitive(boxedValues[index&fixtureMask])),
|
|
181
|
+
makeCase('union/first',true,index=>is.union(numbers[index&fixtureMask],'number|string|boolean')),
|
|
182
|
+
makeCase('union/second',true,index=>is.union(String(index&fixtureMask),'number|string|boolean')),
|
|
183
|
+
makeCase('union/miss',false,index=>weakIs.union(plainObjects[index&fixtureMask],'number|string|boolean'))
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
if(sharedArrayBuffers.length){
|
|
187
|
+
cases.push(makeCase('anyArrayBuffer/pass-shared',true,index=>weakIs.anyArrayBuffer(sharedArrayBuffers[index&fixtureMask])));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if(typeof Intl.DateTimeFormat === 'function'){
|
|
191
|
+
const formats=fixtures(index=>new Intl.DateTimeFormat(index%2 ? 'en' : 'fr'));
|
|
192
|
+
cases.push(makeCase('intlDateTimeFormat/pass',true,index=>weakIs.intlDateTimeFormat(formats[index&fixtureMask])));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if(typeof Intl.Segmenter === 'function'){
|
|
196
|
+
const segments=fixtures(index=>new Intl.Segmenter(index%2 ? 'en' : 'fr').segment(`strong type ${index}`));
|
|
197
|
+
cases.push(makeCase('intlSegments/pass',true,index=>weakIs.intlSegments(segments[index&fixtureMask])));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const resizableArrayBuffers=fixtures(()=>new ArrayBuffer(8,{maxByteLength:16}));
|
|
201
|
+
if(resizableArrayBuffers[0].resizable === true){
|
|
202
|
+
cases.push(makeCase('resizableArrayBuffer/pass',true,index=>weakIs.resizableArrayBuffer(resizableArrayBuffers[index&fixtureMask])));
|
|
203
|
+
}
|
|
204
|
+
return cases;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const comparableCases=function(Class){
|
|
208
|
+
const is=new Class;
|
|
209
|
+
const weakIs=new Class(false);
|
|
210
|
+
return [
|
|
211
|
+
makeCase('defined/pass',true,index=>weakIs.defined(definedValues[index&fixtureMask])),
|
|
212
|
+
makeCase('number/pass',true,index=>weakIs.number(numbers[index&fixtureMask])),
|
|
213
|
+
makeCase('array/pass',true,index=>weakIs.array(arrays[index&fixtureMask])),
|
|
214
|
+
makeCase('date/pass',true,index=>weakIs.date(dates[index&fixtureMask])),
|
|
215
|
+
makeCase('map/pass',true,index=>weakIs.map(maps[index&fixtureMask])),
|
|
216
|
+
makeCase('set/pass',true,index=>weakIs.set(sets[index&fixtureMask])),
|
|
217
|
+
makeCase('regExp/pass',true,index=>weakIs.regExp(regularExpressions[index&fixtureMask])),
|
|
218
|
+
makeCase('uint8Array/pass',true,index=>weakIs.uint8Array(uint8Arrays[index&fixtureMask])),
|
|
219
|
+
makeCase('arrayBuffer/pass',true,index=>weakIs.arrayBuffer(arrayBuffers[index&fixtureMask])),
|
|
220
|
+
makeCase('dataView/pass',true,index=>weakIs.dataView(dataViews[index&fixtureMask])),
|
|
221
|
+
makeCase('union/first',true,index=>is.union(numbers[index&fixtureMask],'number|string')),
|
|
222
|
+
makeCase('union/second',true,index=>is.union(String(index&fixtureMask),'number|string'))
|
|
223
|
+
];
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const git=function(args){
|
|
227
|
+
try{
|
|
228
|
+
return execFileSync('git',args,{encoding:'utf8',stdio:['ignore','pipe','ignore'],windowsHide:true}).trim();
|
|
229
|
+
}catch(err){
|
|
230
|
+
return '';
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const classAtCommit=async function(targetCommit){
|
|
235
|
+
let source;
|
|
236
|
+
try{
|
|
237
|
+
source=execFileSync('git',['show',`${targetCommit}:index.js`],{encoding:'utf8',windowsHide:true});
|
|
238
|
+
}catch(err){
|
|
239
|
+
throw new Error(`unable to load benchmark source from ${targetCommit}`);
|
|
240
|
+
}
|
|
241
|
+
const module=await import(`data:text/javascript;base64,${Buffer.from(source).toString('base64')}#${targetCommit}`);
|
|
242
|
+
return module.default;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const commit=git(['rev-parse','--short','HEAD']) || 'unavailable';
|
|
246
|
+
const revisionState=commit === 'unavailable' ? 'Git unavailable' : git(['status','--porcelain','--untracked-files=no']) ? 'dirty' : 'clean';
|
|
247
|
+
|
|
248
|
+
console.log(`Runtime: Node ${process.version} | V8 ${process.versions.v8} | ${process.platform} ${process.arch}`);
|
|
249
|
+
console.log(`Revision: ${commit} (${revisionState})`);
|
|
250
|
+
console.log(`Method: ${targetSampleMs} ms target | ${sampleCount} samples | dedicated per-case loops | median and median absolute deviation`);
|
|
251
|
+
console.log('Note: ns/op is harness-specific JIT throughput, not end-to-end application latency.');
|
|
252
|
+
|
|
253
|
+
const diagnostics=[
|
|
254
|
+
['number fail/pass','number/fail','number/pass'],
|
|
255
|
+
['array fail/pass','array/fail','array/pass'],
|
|
256
|
+
['null-prototype fail/pass','nullPrototypeObject/fail','nullPrototypeObject/pass'],
|
|
257
|
+
['validDate/date','validDate/pass','date/pass'],
|
|
258
|
+
['uint8Array/arrayBufferView','uint8Array/pass','arrayBufferView/pass'],
|
|
259
|
+
['union second/first','union/second','union/first'],
|
|
260
|
+
['union miss/first','union/miss','union/first']
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
const printCurrentResults=function(results){
|
|
264
|
+
const nameWidth=Math.max(...results.map(result=>result.name.length));
|
|
265
|
+
console.log(`\n${'case'.padEnd(nameWidth)} ${'ns/op'.padStart(12)} ${'ops/sec'.padStart(14)} ${'MAD'.padStart(8)} ${'iterations'.padStart(12)}`);
|
|
266
|
+
for(const result of results){
|
|
267
|
+
console.log(`${result.name.padEnd(nameWidth)} ${result.nanoseconds.toFixed(2).padStart(12)} ${Math.round(result.operationsPerSecond).toLocaleString('en-US').padStart(14)} ${(result.deviationPercent.toFixed(2)+'%').padStart(8)} ${result.iterations.toLocaleString('en-US').padStart(12)}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const byName=new Map(results.map(result=>[result.name,result]));
|
|
271
|
+
console.log('\nDiagnostic ratios');
|
|
272
|
+
for(const [label,numeratorName,denominatorName] of diagnostics){
|
|
273
|
+
const numerator=byName.get(numeratorName);
|
|
274
|
+
const denominator=byName.get(denominatorName);
|
|
275
|
+
if(numerator && denominator){
|
|
276
|
+
console.log(`${label}: ${(numerator.nanoseconds/denominator.nanoseconds).toFixed(2)}x`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const printPairs=function(pairs,baselineLabel){
|
|
282
|
+
const nameWidth=Math.max(...pairs.map(pair=>pair.current.name.length));
|
|
283
|
+
console.log(`${'case'.padEnd(nameWidth)} ${'current ns'.padStart(12)} ${'current MAD'.padStart(11)} ${baselineLabel.padStart(12)} ${'base MAD'.padStart(9)} ${'speedup'.padStart(10)}`);
|
|
284
|
+
for(const pair of pairs){
|
|
285
|
+
const speedup=pair.baseline.nanoseconds/pair.current.nanoseconds;
|
|
286
|
+
console.log(`${pair.current.name.padEnd(nameWidth)} ${pair.current.nanoseconds.toFixed(2).padStart(12)} ${(pair.current.deviationPercent.toFixed(2)+'%').padStart(11)} ${pair.baseline.nanoseconds.toFixed(2).padStart(12)} ${(pair.baseline.deviationPercent.toFixed(2)+'%').padStart(9)} ${(speedup.toFixed(2)+'x').padStart(10)}`);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const runComparisons=async function(){
|
|
291
|
+
if(!baseline && !historical){
|
|
292
|
+
printCurrentResults(currentCases(Is).map(benchmark));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if(baseline){
|
|
296
|
+
const BaselineIs=await classAtCommit(baselineCommit);
|
|
297
|
+
const currentBaselineCases=currentCases(Is);
|
|
298
|
+
const baselineCases=currentCases(BaselineIs);
|
|
299
|
+
const pairs=currentBaselineCases.map((current,index)=>benchmarkPair(current,baselineCases[index],Boolean(index%2)));
|
|
300
|
+
console.log(`\nPre-optimization comparison: current vs ${baselineCommit}`);
|
|
301
|
+
printPairs(pairs,'baseline ns');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if(historical){
|
|
305
|
+
const HistoricalIs=await classAtCommit(historicalCommit);
|
|
306
|
+
const currentComparable=comparableCases(Is);
|
|
307
|
+
const baselineComparable=comparableCases(HistoricalIs);
|
|
308
|
+
const pairs=currentComparable.map((current,index)=>benchmarkPair(current,baselineComparable[index],Boolean(index%2)));
|
|
309
|
+
console.log(`\nHistorical throughput reference: current vs 1.1.0 (${historicalCommit})`);
|
|
310
|
+
console.log('These same-realm positive paths overlap behaviorally; 1.1.0 does not provide the current cross-realm and spoof guarantees.');
|
|
311
|
+
printPairs(pairs,'1.1.0 ns');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if(compareConversions){
|
|
315
|
+
throw new Error(`compare/pass stringified its successful target ${compareConversions.toLocaleString('en-US')} times`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log(`\nCorrectness checksum: ${sink>>>0}`);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
runComparisons().catch(err=>{
|
|
322
|
+
console.error(err && err.stack ? err.stack : err);
|
|
323
|
+
process.exitCode=1;
|
|
324
|
+
});
|