@sdeverywhere/plugin-check 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/dist/index.cjs +519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +496 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
- package/template-bundle/package.json +8 -0
- package/template-bundle/src/bundle.ts +172 -0
- package/template-bundle/src/empty-model-spec.ts +15 -0
- package/template-bundle/src/index.ts +3 -0
- package/template-bundle/src/inputs.ts +139 -0
- package/template-bundle/src/outputs.ts +36 -0
- package/template-bundle/src/worker.js +9 -0
- package/template-bundle/tsconfig.json +27 -0
- package/template-report/index.html +27 -0
- package/template-report/package.json +6 -0
- package/template-report/src/empty-bundle.ts +23 -0
- package/template-report/src/empty-test-config.ts +24 -0
- package/template-report/src/env.d.ts +7 -0
- package/template-report/src/global.css +22 -0
- package/template-report/src/index.ts +56 -0
- package/template-report/src/messages.html +6 -0
- package/template-report/src/overlay.ts +32 -0
- package/template-report/src/url-polyfill.ts +10 -0
- package/template-report/tsconfig.json +31 -0
- package/template-tests/package.json +5 -0
- package/template-tests/src/env.d.ts +5 -0
- package/template-tests/src/index.ts +82 -0
- package/template-tests/tsconfig.json +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sdeverywhere/plugin-check",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist/**",
|
|
6
|
+
"template-bundle/**",
|
|
7
|
+
"template-report/**",
|
|
8
|
+
"template-tests/**"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "dist/index.cjs",
|
|
12
|
+
"module": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
23
|
+
"@rollup/plugin-replace": "^2.4.1",
|
|
24
|
+
"@sdeverywhere/build": "^0.1.0",
|
|
25
|
+
"@sdeverywhere/check-core": "^0.1.0",
|
|
26
|
+
"@sdeverywhere/check-ui-shell": "^0.1.0",
|
|
27
|
+
"@sdeverywhere/runtime": "^0.1.0",
|
|
28
|
+
"@sdeverywhere/runtime-async": "^0.1.0",
|
|
29
|
+
"assert-never": "^1.2.1",
|
|
30
|
+
"picocolors": "^1.0.0",
|
|
31
|
+
"rollup-plugin-node-polyfills": "^0.2.1",
|
|
32
|
+
"vite": "^2.9.12",
|
|
33
|
+
"vite-plugin-glob": "^0.3.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^16.11.7"
|
|
37
|
+
},
|
|
38
|
+
"author": "Climate Interactive",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"homepage": "https://sdeverywhere.org",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/climateinteractive/SDEverywhere.git",
|
|
44
|
+
"directory": "packages/plugin-check"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/climateinteractive/SDEverywhere/issues"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"clean": "rm -rf dist",
|
|
51
|
+
"lint": "eslint src --ext .ts --max-warnings 0",
|
|
52
|
+
"prettier:check": "prettier --check .",
|
|
53
|
+
"prettier:fix": "prettier --write .",
|
|
54
|
+
"precommit": "../../scripts/precommit",
|
|
55
|
+
"test": "echo No tests yet",
|
|
56
|
+
"test:watch": "echo No tests yet",
|
|
57
|
+
"test:ci": "echo No tests yet",
|
|
58
|
+
"type-check": "tsc --noEmit -p tsconfig-build.json",
|
|
59
|
+
"build": "tsup",
|
|
60
|
+
"ci:build": "run-s clean lint prettier:check test:ci type-check build"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
Bundle,
|
|
5
|
+
BundleGraphData,
|
|
6
|
+
BundleModel as CheckBundleModel,
|
|
7
|
+
Dataset,
|
|
8
|
+
DatasetKey,
|
|
9
|
+
DatasetMap,
|
|
10
|
+
DatasetsResult,
|
|
11
|
+
LinkItem,
|
|
12
|
+
ModelSpec,
|
|
13
|
+
Scenario
|
|
14
|
+
} from '@sdeverywhere/check-core'
|
|
15
|
+
|
|
16
|
+
import type { InputValue, InputVarId, ModelRunner, Point } from '@sdeverywhere/runtime'
|
|
17
|
+
import { Outputs } from '@sdeverywhere/runtime'
|
|
18
|
+
import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async'
|
|
19
|
+
|
|
20
|
+
import type { Input } from './inputs'
|
|
21
|
+
import { getInputVars, setInputsForScenario } from './inputs'
|
|
22
|
+
import { getOutputVars } from './outputs'
|
|
23
|
+
|
|
24
|
+
import { startTime, endTime, inputSpecs, outputSpecs } from 'virtual:model-spec'
|
|
25
|
+
|
|
26
|
+
import modelWorkerJs from '@_model_worker_/worker.js?raw'
|
|
27
|
+
|
|
28
|
+
// The current version of the check bundle format. This should be
|
|
29
|
+
// incremented when there is an incompatible change to the bundle format.
|
|
30
|
+
// The model-check tools can use this value to skip tests if two bundles
|
|
31
|
+
// have different version numbers.
|
|
32
|
+
const VERSION = 1
|
|
33
|
+
|
|
34
|
+
// The size (in bytes) of the model file(s), injected at build time.
|
|
35
|
+
const __MODEL_SIZE_IN_BYTES__ = 1
|
|
36
|
+
const modelSizeInBytes = __MODEL_SIZE_IN_BYTES__
|
|
37
|
+
|
|
38
|
+
// The size (in bytes) of the data file(s), injected at build time.
|
|
39
|
+
const __DATA_SIZE_IN_BYTES__ = 1
|
|
40
|
+
const dataSizeInBytes = __DATA_SIZE_IN_BYTES__
|
|
41
|
+
|
|
42
|
+
export class BundleModel implements CheckBundleModel {
|
|
43
|
+
private readonly inputs: InputValue[]
|
|
44
|
+
private outputs: Outputs
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param modelSpec The spec for the bundled model.
|
|
48
|
+
* @param inputMap The model inputs.
|
|
49
|
+
* @param modelRunner The model runner.
|
|
50
|
+
*/
|
|
51
|
+
constructor(
|
|
52
|
+
public readonly modelSpec: ModelSpec,
|
|
53
|
+
private readonly inputMap: Map<InputVarId, Input>,
|
|
54
|
+
private readonly modelRunner: ModelRunner
|
|
55
|
+
) {
|
|
56
|
+
// Derive an array of `InputValue` instances that can be passed to the runner
|
|
57
|
+
this.inputs = [...inputMap.values()].map(input => input.value)
|
|
58
|
+
|
|
59
|
+
// Create an `Outputs` instance that is initialized to hold output data
|
|
60
|
+
// produced by the Wasm model
|
|
61
|
+
const outputVarIds = outputSpecs.map(o => o.varId)
|
|
62
|
+
this.outputs = new Outputs(outputVarIds, startTime, endTime)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// from CheckBundleModel interface
|
|
66
|
+
async getDatasetsForScenario(scenario: Scenario, datasetKeys: DatasetKey[]): Promise<DatasetsResult> {
|
|
67
|
+
const datasetMap: DatasetMap = new Map()
|
|
68
|
+
|
|
69
|
+
// Set the input values according to the given scenario
|
|
70
|
+
setInputsForScenario(this.inputMap, scenario)
|
|
71
|
+
|
|
72
|
+
// Run the JS model
|
|
73
|
+
this.outputs = await this.modelRunner.runModel(this.inputs, this.outputs)
|
|
74
|
+
const modelRunTime = this.outputs.runTimeInMillis
|
|
75
|
+
|
|
76
|
+
// Extract the data for each requested output variable and put it into a map
|
|
77
|
+
for (const datasetKey of datasetKeys) {
|
|
78
|
+
// Get the output variable for the given dataset key; if the variable doesn't
|
|
79
|
+
// exist in this version of the model/bundle, just skip it
|
|
80
|
+
const outputVar = this.modelSpec.outputVars.get(datasetKey)
|
|
81
|
+
if (!outputVar) {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (outputVar.sourceName === undefined) {
|
|
86
|
+
// See if we have data for the requested model output variable
|
|
87
|
+
const series = this.outputs.getSeriesForVar(outputVar.varId)
|
|
88
|
+
if (series) {
|
|
89
|
+
datasetMap.set(datasetKey, datasetFromPoints(series.points))
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
console.error('Static data sources not yet handled in default model check bundle')
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
datasetMap,
|
|
98
|
+
modelRunTime
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// from CheckBundleModel interface
|
|
103
|
+
// TODO: This function should be optional
|
|
104
|
+
async getGraphDataForScenario(): Promise<BundleGraphData> {
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// from CheckBundleModel interface
|
|
109
|
+
// TODO: This function should be optional
|
|
110
|
+
getGraphLinksForScenario(): LinkItem[] {
|
|
111
|
+
return []
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Initialize a `BundleModel` instance that supports the running the model
|
|
117
|
+
* under different scenarios.
|
|
118
|
+
*/
|
|
119
|
+
async function initBundleModel(modelSpec: ModelSpec, inputMap: Map<InputVarId, Input>): Promise<BundleModel> {
|
|
120
|
+
// Initialize the Wasm model asynchronously. We inline the worker code in the
|
|
121
|
+
// rolled-up bundle so that we don't have to fetch a separate `worker.js` file
|
|
122
|
+
const modelRunner = await spawnAsyncModelRunner({ source: modelWorkerJs })
|
|
123
|
+
|
|
124
|
+
// Return a `BundleModel` that wraps the underlying config and Wasm model
|
|
125
|
+
return new BundleModel(modelSpec, inputMap, modelRunner)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a `Dataset` containing the given data points from the model.
|
|
130
|
+
*/
|
|
131
|
+
export function datasetFromPoints(points: Point[]): Dataset {
|
|
132
|
+
const dataMap = new Map()
|
|
133
|
+
for (const point of points) {
|
|
134
|
+
// We omit points that have an undefined value. SDE represents `:NA:` values as
|
|
135
|
+
// a special value (`-DBL_MAX`); the `runtime` package detects these and converts
|
|
136
|
+
// them to undefined instead. We don't need them to appear in graphs, so omit them.
|
|
137
|
+
if (point.y !== undefined) {
|
|
138
|
+
dataMap.set(point.x, point.y)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return dataMap
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Return a `Bundle` that can be used for running comparisons between two
|
|
146
|
+
* bundles containing different versions of the sample model.
|
|
147
|
+
*/
|
|
148
|
+
export function createBundle(): Bundle {
|
|
149
|
+
// Gather information about the input and output variables used in the model
|
|
150
|
+
const inputVars = getInputVars(inputSpecs)
|
|
151
|
+
const outputVars = getOutputVars(outputSpecs)
|
|
152
|
+
|
|
153
|
+
const modelSpec: ModelSpec = {
|
|
154
|
+
modelSizeInBytes,
|
|
155
|
+
dataSizeInBytes,
|
|
156
|
+
inputVars,
|
|
157
|
+
outputVars,
|
|
158
|
+
implVars: new Map(),
|
|
159
|
+
inputGroups: new Map(),
|
|
160
|
+
datasetGroups: new Map(),
|
|
161
|
+
startTime,
|
|
162
|
+
endTime
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
version: VERSION,
|
|
167
|
+
modelSpec,
|
|
168
|
+
initModel: () => {
|
|
169
|
+
return initBundleModel(modelSpec, inputVars)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* This no-op module is configured as the default path alias in the tsconfig
|
|
5
|
+
* files so that TypeScript does not complain. (The actual module will be set
|
|
6
|
+
* up using aliases in the Vite config file.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { InputSpec } from './inputs'
|
|
10
|
+
import type { OutputSpec } from './outputs'
|
|
11
|
+
|
|
12
|
+
export const startTime = 0
|
|
13
|
+
export const endTime = 0
|
|
14
|
+
export const inputSpecs: InputSpec[] = []
|
|
15
|
+
export const outputSpecs: OutputSpec[] = []
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
import { assertNever } from 'assert-never'
|
|
4
|
+
|
|
5
|
+
import type { InputVar, Scenario } from '@sdeverywhere/check-core'
|
|
6
|
+
import type { InputValue, InputVarId } from '@sdeverywhere/runtime'
|
|
7
|
+
import { createInputValue } from '@sdeverywhere/runtime'
|
|
8
|
+
|
|
9
|
+
export interface InputSpec {
|
|
10
|
+
/** The variable identifier (as used by SDEverywhere). */
|
|
11
|
+
varId: string
|
|
12
|
+
/** The variable name (as used in the modeling tool). */
|
|
13
|
+
varName: string
|
|
14
|
+
/** The default value for the input. */
|
|
15
|
+
defaultValue: number
|
|
16
|
+
/** The minimum value for the input. */
|
|
17
|
+
minValue: number
|
|
18
|
+
/** The maximum value for the input. */
|
|
19
|
+
maxValue: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Input extends InputVar {
|
|
23
|
+
value: InputValue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gather the set of input variables used in this version of the model.
|
|
28
|
+
*/
|
|
29
|
+
export function getInputVars(inputSpecs: InputSpec[]): Map<InputVarId, Input> {
|
|
30
|
+
const inputs: Map<InputVarId, Input> = new Map()
|
|
31
|
+
for (const inputSpec of inputSpecs) {
|
|
32
|
+
const varId = inputSpec.varId
|
|
33
|
+
const input: Input = {
|
|
34
|
+
varId,
|
|
35
|
+
varName: inputSpec.varName,
|
|
36
|
+
defaultValue: inputSpec.defaultValue,
|
|
37
|
+
minValue: inputSpec.minValue,
|
|
38
|
+
maxValue: inputSpec.maxValue,
|
|
39
|
+
value: createInputValue(varId, inputSpec.defaultValue)
|
|
40
|
+
}
|
|
41
|
+
inputs.set(varId, input)
|
|
42
|
+
}
|
|
43
|
+
return inputs
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set the given `Input` instances according to the given scenario.
|
|
48
|
+
*/
|
|
49
|
+
export function setInputsForScenario(inputs: Map<InputVarId, Input>, scenario: Scenario): void {
|
|
50
|
+
function setInputToValue(input: Input, value: number): void {
|
|
51
|
+
if (value < input.minValue) {
|
|
52
|
+
// TODO: Set an error status so that the scenario is flagged as an
|
|
53
|
+
// error in the UI (for now, just warn and clamp)
|
|
54
|
+
console.warn(
|
|
55
|
+
`WARNING: Scenario input value ${value} is < min value (${input.minValue}) ` + `for input '${input.varName}'`
|
|
56
|
+
)
|
|
57
|
+
value = input.minValue
|
|
58
|
+
} else if (value > input.maxValue) {
|
|
59
|
+
console.warn(
|
|
60
|
+
`WARNING: Scenario input value ${value} is > max value (${input.maxValue}) ` + `for input '${input.varName}'`
|
|
61
|
+
)
|
|
62
|
+
value = input.maxValue
|
|
63
|
+
}
|
|
64
|
+
input.value.set(value)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function setInputToDefault(input: Input): void {
|
|
68
|
+
input.value.reset()
|
|
69
|
+
}
|
|
70
|
+
function setInputToMinimum(input: Input): void {
|
|
71
|
+
input.value.set(input.minValue)
|
|
72
|
+
}
|
|
73
|
+
function setInputToMaximum(input: Input): void {
|
|
74
|
+
input.value.set(input.minValue)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function setAllToDefault(): void {
|
|
78
|
+
inputs.forEach(setInputToDefault)
|
|
79
|
+
}
|
|
80
|
+
function setAllToMinimum(): void {
|
|
81
|
+
inputs.forEach(setInputToMinimum)
|
|
82
|
+
}
|
|
83
|
+
function setAllToMaximum(): void {
|
|
84
|
+
inputs.forEach(setInputToMaximum)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Set inputs according to the given scenario
|
|
88
|
+
switch (scenario.kind) {
|
|
89
|
+
case 'all-inputs': {
|
|
90
|
+
switch (scenario.position) {
|
|
91
|
+
case 'at-default':
|
|
92
|
+
setAllToDefault()
|
|
93
|
+
break
|
|
94
|
+
case 'at-minimum':
|
|
95
|
+
setAllToMinimum()
|
|
96
|
+
break
|
|
97
|
+
case 'at-maximum':
|
|
98
|
+
setAllToMaximum()
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
case 'settings': {
|
|
104
|
+
setAllToDefault()
|
|
105
|
+
for (const setting of scenario.settings) {
|
|
106
|
+
const input = inputs.get(setting.inputVarId)
|
|
107
|
+
if (input) {
|
|
108
|
+
switch (setting.kind) {
|
|
109
|
+
case 'position':
|
|
110
|
+
switch (setting.position) {
|
|
111
|
+
case 'at-default':
|
|
112
|
+
setInputToDefault(input)
|
|
113
|
+
break
|
|
114
|
+
case 'at-minimum':
|
|
115
|
+
setInputToMinimum(input)
|
|
116
|
+
break
|
|
117
|
+
case 'at-maximum':
|
|
118
|
+
setInputToMaximum(input)
|
|
119
|
+
break
|
|
120
|
+
default:
|
|
121
|
+
assertNever(setting.position)
|
|
122
|
+
}
|
|
123
|
+
break
|
|
124
|
+
case 'value':
|
|
125
|
+
setInputToValue(input, setting.value)
|
|
126
|
+
break
|
|
127
|
+
default:
|
|
128
|
+
assertNever(setting)
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`No model input for scenario input ${setting.inputVarId}`)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
break
|
|
135
|
+
}
|
|
136
|
+
default:
|
|
137
|
+
assertNever(scenario)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
import type { DatasetKey, OutputVar, SourceName } from '@sdeverywhere/check-core'
|
|
4
|
+
import type { OutputVarId } from '@sdeverywhere/runtime'
|
|
5
|
+
|
|
6
|
+
export interface OutputSpec {
|
|
7
|
+
/** The variable identifier (as used by SDEverywhere). */
|
|
8
|
+
varId: string
|
|
9
|
+
/** The variable name (as used in the modeling tool). */
|
|
10
|
+
varName: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gather the list of output variables (and their related graphs, etc) used
|
|
15
|
+
* in this version of the model.
|
|
16
|
+
*/
|
|
17
|
+
export function getOutputVars(outputSpecs: OutputSpec[]): Map<DatasetKey, OutputVar> {
|
|
18
|
+
// Convert the specs to `OutputVar` instances
|
|
19
|
+
const outputVars: Map<DatasetKey, OutputVar> = new Map()
|
|
20
|
+
|
|
21
|
+
for (const outputSpec of outputSpecs) {
|
|
22
|
+
const varId = outputSpec.varId
|
|
23
|
+
const datasetKey = datasetKeyForOutputVar(undefined, varId)
|
|
24
|
+
outputVars.set(datasetKey, {
|
|
25
|
+
sourceName: undefined,
|
|
26
|
+
varId,
|
|
27
|
+
varName: outputSpec.varName
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return outputVars
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function datasetKeyForOutputVar(sourceName: SourceName | undefined, varId: OutputVarId): DatasetKey {
|
|
35
|
+
return `${sourceName || 'Model'}_${varId}`
|
|
36
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* This no-op module is configured as the default path alias in the tsconfig
|
|
5
|
+
* files so that TypeScript does not complain. (The actual module will be set
|
|
6
|
+
* up using aliases in the Vite config file.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const unused = 0
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es6",
|
|
4
|
+
// Use "es2020" for dynamic import
|
|
5
|
+
"module": "es2020",
|
|
6
|
+
// Emit additional JS to ease support for importing CommonJS modules
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
// Use Node.js-style module resolution
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
// Enable warnings for transpilation-unsafe code
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
// Enable strict enforcement of `import type`
|
|
13
|
+
"importsNotUsedAsValues": "error",
|
|
14
|
+
"noImplicitAny": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"types": ["vite/client"],
|
|
18
|
+
// Use placeholders for path aliases that are configured in the Vite
|
|
19
|
+
// config; this prevents TypeScript from complaining but still allows
|
|
20
|
+
// for plugging in "real" files later
|
|
21
|
+
"baseUrl": ".",
|
|
22
|
+
"paths": {
|
|
23
|
+
"virtual:model-spec": ["./src/empty-model-spec.ts"],
|
|
24
|
+
"@_model_worker_": ["./src"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Model Check</title>
|
|
5
|
+
|
|
6
|
+
<meta charset="UTF-8" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
11
|
+
<link
|
|
12
|
+
href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@400&family=Roboto:wght@500;700&display=swap"
|
|
13
|
+
rel="stylesheet"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
<script type="module" src="./src/index.ts"></script>
|
|
17
|
+
</head>
|
|
18
|
+
|
|
19
|
+
<body>
|
|
20
|
+
<div id="app-shell-container"></div>
|
|
21
|
+
<div id="overlay-container">
|
|
22
|
+
<div id="overlay-content">
|
|
23
|
+
<div id="overlay-text"></div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
import type { Bundle } from '@sdeverywhere/check-core'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* This module serves two purposes:
|
|
7
|
+
*
|
|
8
|
+
* 1. It is configured as the default path alias in the tsconfig files so
|
|
9
|
+
* that TypeScript does not complain. (The actual bundles will be set
|
|
10
|
+
* up using aliases in the Vite config file.)
|
|
11
|
+
*
|
|
12
|
+
* 2. It is used as a default no-op bundle in the case where a baseline
|
|
13
|
+
* bundle is not used (in which case no comparison tests will be run
|
|
14
|
+
* and only the current bundle will be checked).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export function createBundle(): Bundle {
|
|
18
|
+
return {
|
|
19
|
+
version: -1,
|
|
20
|
+
modelSpec: undefined,
|
|
21
|
+
initModel: () => undefined
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* This no-op module is configured as the default path alias in the tsconfig
|
|
5
|
+
* files so that TypeScript does not complain. (The actual module will be set
|
|
6
|
+
* up using aliases in the Vite config file.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Bundle, ConfigOptions } from '@sdeverywhere/check-core'
|
|
10
|
+
|
|
11
|
+
export interface BundleOptions {
|
|
12
|
+
nameL?: string
|
|
13
|
+
nameR?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getConfigOptions(
|
|
17
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
18
|
+
_bundleL: Bundle | undefined,
|
|
19
|
+
_bundleR: Bundle,
|
|
20
|
+
_opts: BundleOptions
|
|
21
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
22
|
+
): Promise<ConfigOptions> {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
// These values are injected by Vite at build time, so we need to
|
|
4
|
+
// declare types for them here
|
|
5
|
+
declare const __BASELINE_NAME__: string
|
|
6
|
+
declare const __CURRENT_NAME__: string
|
|
7
|
+
declare const __SUITE_SUMMARY_JSON__: string
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#overlay-content {
|
|
2
|
+
position: fixed;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
flex: 1;
|
|
6
|
+
bottom: 0;
|
|
7
|
+
right: 0;
|
|
8
|
+
margin-right: 1rem;
|
|
9
|
+
margin-bottom: 1rem;
|
|
10
|
+
max-width: 80%;
|
|
11
|
+
max-height: 80%;
|
|
12
|
+
overflow-y: auto;
|
|
13
|
+
padding: 1rem;
|
|
14
|
+
border-radius: 0.5rem;
|
|
15
|
+
background-color: #ddd;
|
|
16
|
+
color: #000;
|
|
17
|
+
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.8);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#overlay-content .overlay-error {
|
|
21
|
+
color: crimson;
|
|
22
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright (c) 2022 Climate Interactive / New Venture Fund
|
|
2
|
+
|
|
3
|
+
import type { Bundle, SuiteSummary } from '@sdeverywhere/check-core'
|
|
4
|
+
|
|
5
|
+
import { initAppShell } from '@sdeverywhere/check-ui-shell'
|
|
6
|
+
import '@sdeverywhere/check-ui-shell/dist/style.css'
|
|
7
|
+
|
|
8
|
+
import { initOverlay } from './overlay'
|
|
9
|
+
|
|
10
|
+
import './global.css'
|
|
11
|
+
|
|
12
|
+
// These aliases are specified in the Vite config to point to the actual bundles
|
|
13
|
+
import { createBundle as createBaselineBundle } from '@_baseline_bundle_'
|
|
14
|
+
import { createBundle as createCurrentBundle } from '@_current_bundle_'
|
|
15
|
+
import { getConfigOptions } from '@_test_config_'
|
|
16
|
+
|
|
17
|
+
// For "production" builds, load the summary from a JSON file that
|
|
18
|
+
// was generated as part of the build process. This makes the
|
|
19
|
+
// report load almost immediately instead of running all the checks
|
|
20
|
+
// in the user's browser.
|
|
21
|
+
const suiteSummaryJson = __SUITE_SUMMARY_JSON__
|
|
22
|
+
let suiteSummary: SuiteSummary
|
|
23
|
+
if (suiteSummaryJson) {
|
|
24
|
+
suiteSummary = JSON.parse(suiteSummaryJson) as SuiteSummary
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function init() {
|
|
28
|
+
// Load the bundles used by the model check/compare configuration. We
|
|
29
|
+
// always initialize the "current" bundle.
|
|
30
|
+
const bundleR: Bundle = createCurrentBundle()
|
|
31
|
+
|
|
32
|
+
// Only initialize the "baseline" bundle if it is defined and the version
|
|
33
|
+
// is the same as the "current" one. If the baseline bundle has a different
|
|
34
|
+
// version, we will skip the comparison tests and only run the checks on the
|
|
35
|
+
// current bundle.
|
|
36
|
+
let bundleL: Bundle
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
const rawBundleL: any = createBaselineBundle()
|
|
39
|
+
if (rawBundleL.version === bundleR.version) {
|
|
40
|
+
bundleL = rawBundleL as Bundle
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Prepare the model check/compare configuration
|
|
44
|
+
const checkOptions = await getConfigOptions(bundleL, bundleR, {
|
|
45
|
+
nameL: __BASELINE_NAME__ || undefined,
|
|
46
|
+
nameR: __CURRENT_NAME__
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Initialize the root Svelte component
|
|
50
|
+
initAppShell(checkOptions, suiteSummary)
|
|
51
|
+
|
|
52
|
+
// Initialize the overlay element used to show builder messages/errors
|
|
53
|
+
initOverlay()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
init()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
NOTE: This file is only referenced by `tsconfig.json` for the purposes of
|
|
3
|
+
configuring the '@_prep_' path alias; it is not included with the report
|
|
4
|
+
build. In the Vite config, we will use a different alias that points to
|
|
5
|
+
the actual `messages.html` file under the configured sde "prep" directory.
|
|
6
|
+
-->
|