@slim-lang/core 1.2.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/README.md +666 -0
- package/package.json +55 -0
- package/packages/slim/.spm +7 -0
- package/packages/slim/converters/main.slim +106 -0
- package/packages/slim/helpers/array.slim +25 -0
- package/packages/slim/helpers/path.slim +3 -0
- package/packages/slim/helpers/request.slim +102 -0
- package/packages/slim/helpers/string.slim +27 -0
- package/packages/slim/main.slim +42 -0
- package/packages/slim/parse/main.slim +25 -0
- package/packages/slim/server/main.slim +423 -0
- package/packages/slim/time/main.slim +66 -0
- package/packages/slim/types/common.slim +6 -0
- package/packages/slim/types/formats.slim +23 -0
- package/packages/slim/types/hash.slim +6 -0
- package/packages/slim/types/mails.slim +3 -0
- package/packages/slim/types/numerical.slim +9 -0
- package/packages/slim/types/time.slim +3 -0
- package/run-dev-slim.js +133 -0
- package/run-slim.js +20 -0
- package/src/bin/api/github_auth.js +89 -0
- package/src/bin/api/github_get.js +139 -0
- package/src/bin/api/github_req.js +455 -0
- package/src/bin/api/lock.js +37 -0
- package/src/bin/api/spm.js +103 -0
- package/src/bin/api/storage.js +30 -0
- package/src/bin/cli.js +404 -0
- package/src/bin/config.default.json +5 -0
- package/src/bin/helpers.js +147 -0
- package/src/bin/parsers/spm.js +174 -0
- package/src/bin/spm.js +519 -0
- package/src/checker.js +926 -0
- package/src/compile.js +230 -0
- package/src/external/classErrors.js +202 -0
- package/src/external/client.js +38 -0
- package/src/external/core.js +861 -0
- package/src/external/defaults.js +25 -0
- package/src/external/helpers.js +541 -0
- package/src/external/slim-globals.d.ts +65 -0
- package/src/external/types.js +38 -0
- package/src/format.js +81 -0
- package/src/handlers/errorHandler.js +43 -0
- package/src/handlers/parser/components.js +250 -0
- package/src/handlers/parserHandler.js +793 -0
- package/src/jsdoc.js +273 -0
- package/src/lexer.js +174 -0
- package/src/modulePaths.js +74 -0
- package/src/parser.js +818 -0
- package/src/repl.js +32 -0
- package/src/sourcemap.js +0 -0
- package/src/test-runner.js +62 -0
- package/src/transform.js +765 -0
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
// Portable core runtime with no Node or DOM dependencies.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
StructPassedError, StructError, StructExpectError, ArgumentDeclarationTypeError,
|
|
5
|
+
EnumError
|
|
6
|
+
} from "./classErrors.js"
|
|
7
|
+
import {
|
|
8
|
+
SlimVariableType, SlimVariableTypes, Struct, Component, Enum, EnumValue,
|
|
9
|
+
isSameType
|
|
10
|
+
} from "./types.js"
|
|
11
|
+
import { formatError, resolveErrorLocation } from "./classErrors.js"
|
|
12
|
+
|
|
13
|
+
const __structs__ = {}
|
|
14
|
+
const __enums__ = {}
|
|
15
|
+
const __RESERVED_DEFINES__ = new Set([
|
|
16
|
+
"Error", "Object", "Array", "String", "Number",
|
|
17
|
+
"Boolean", "Function", "Symbol", "Map", "Set",
|
|
18
|
+
"Promise", "Proxy", "Reflect", "Math", "JSON",
|
|
19
|
+
"Date", "RegExp", "WeakMap", "WeakSet", "WeakRef",
|
|
20
|
+
"ArrayBuffer", "DataView", "Iterator",
|
|
21
|
+
"Int8Array", "Uint8Array", "Uint8ClampedArray",
|
|
22
|
+
"Int16Array", "Uint16Array", "Int32Array",
|
|
23
|
+
"Uint32Array", "Float32Array", "Float64Array",
|
|
24
|
+
"undefined", "null", "NaN", "Infinity",
|
|
25
|
+
"globalThis", "global", "process", "console",
|
|
26
|
+
"setTimeout", "setInterval", "clearTimeout", "clearInterval",
|
|
27
|
+
"queueMicrotask", "structuredClone",
|
|
28
|
+
"eval", "isNaN", "isFinite", "parseFloat", "parseInt",
|
|
29
|
+
"decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent",
|
|
30
|
+
"type", "schemeArray", "verify", "values", "verifySafe"
|
|
31
|
+
])
|
|
32
|
+
export const __custom_types__ = {}
|
|
33
|
+
|
|
34
|
+
const RELEASE = typeof process !== "undefined" && process.env?.SLIM_RELEASE === "1"
|
|
35
|
+
|
|
36
|
+
function writeError(text) {
|
|
37
|
+
if (typeof process !== "undefined" && process.stderr) process.stderr.write(text)
|
|
38
|
+
else console.error(text)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function reportError(err) {
|
|
42
|
+
const loc = resolveErrorLocation(err)
|
|
43
|
+
writeError(formatError(
|
|
44
|
+
err?.tag ?? err?.name ?? "Error",
|
|
45
|
+
err?.message ?? String(err),
|
|
46
|
+
loc?.file ?? null,
|
|
47
|
+
loc?.line ?? null,
|
|
48
|
+
loc?.col ?? null,
|
|
49
|
+
loc?.sourceLine ?? null,
|
|
50
|
+
) + "\n")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let __error_handler__ = null
|
|
54
|
+
|
|
55
|
+
export function onError(handler) {
|
|
56
|
+
__error_handler__ = typeof handler === "function" ? handler : null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function finalizeError(err) {
|
|
60
|
+
if (__error_handler__) {
|
|
61
|
+
__error_handler__(err)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
reportError(err)
|
|
65
|
+
if (typeof process !== "undefined" && typeof process.exit === "function") {
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function __handle_async_error__(err) {
|
|
71
|
+
finalizeError(err)
|
|
72
|
+
}
|
|
73
|
+
export function __handle_sync_error__(err) {
|
|
74
|
+
finalizeError(err)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function logProcessed(args) {
|
|
78
|
+
return args.map(arg => {
|
|
79
|
+
if (Type.isTyped(arg) && "value" in arg) {
|
|
80
|
+
return arg.value
|
|
81
|
+
}
|
|
82
|
+
if (Type.isTyped(arg) && "scheme" in arg) {
|
|
83
|
+
return arg.scheme
|
|
84
|
+
}
|
|
85
|
+
return arg
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
export const log = (...args) => {
|
|
89
|
+
const processed = logProcessed(args)
|
|
90
|
+
console.log(...processed)
|
|
91
|
+
}
|
|
92
|
+
export const warn = (...args) => {
|
|
93
|
+
const processed = logProcessed(args)
|
|
94
|
+
console.warn(...processed)
|
|
95
|
+
}
|
|
96
|
+
export const error = (...args) => {
|
|
97
|
+
const processed = logProcessed(args)
|
|
98
|
+
console.error(...processed)
|
|
99
|
+
}
|
|
100
|
+
export const info = (...args) => {
|
|
101
|
+
const processed = logProcessed(args)
|
|
102
|
+
console.info(...processed)
|
|
103
|
+
}
|
|
104
|
+
export const debug = (...args) => {
|
|
105
|
+
console.log(...args)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const PI = Math.PI
|
|
109
|
+
|
|
110
|
+
const __test_stats__ = { passed: 0, failed: 0 }
|
|
111
|
+
|
|
112
|
+
export function test(name, fn) {
|
|
113
|
+
try {
|
|
114
|
+
fn()
|
|
115
|
+
__test_stats__.passed++
|
|
116
|
+
console.log(` ✓ ${name}`)
|
|
117
|
+
} catch (err) {
|
|
118
|
+
__test_stats__.failed++
|
|
119
|
+
if (typeof process !== "undefined") process.exitCode = 1
|
|
120
|
+
console.log(` ✗ ${name}\n ${err?.message ?? err}`)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function assert(condition, message = "assertion failed") {
|
|
125
|
+
if (!condition) throw new Error(message)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function assertEqual(actual, expected, message) {
|
|
129
|
+
if (actual !== expected) {
|
|
130
|
+
throw new Error(message ?? `expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
135
|
+
process.on("exit", () => {
|
|
136
|
+
const total = __test_stats__.passed + __test_stats__.failed
|
|
137
|
+
if (total > 0) console.log(`\n${__test_stats__.passed} passed, ${__test_stats__.failed} failed`)
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class Type {
|
|
142
|
+
static isStruct(obj) {
|
|
143
|
+
return type(obj) == "struct"
|
|
144
|
+
}
|
|
145
|
+
static isEnum(obj) {
|
|
146
|
+
return type(obj) == "enum"
|
|
147
|
+
}
|
|
148
|
+
static isEnumValue(obj) {
|
|
149
|
+
return obj instanceof EnumValue
|
|
150
|
+
}
|
|
151
|
+
static isObj(obj) {
|
|
152
|
+
return type(obj) == "object"
|
|
153
|
+
}
|
|
154
|
+
static isTyped(obj) {
|
|
155
|
+
if (obj instanceof SlimVariableType || obj instanceof EnumValue || obj instanceof Struct) {
|
|
156
|
+
return true
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
static isAnyArray(obj) {
|
|
163
|
+
if (type(obj) == "array") return true
|
|
164
|
+
else if (type(obj).endsWith("[]")) return true
|
|
165
|
+
else return false
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static isRegistredCustom(type) {
|
|
169
|
+
return Object.keys(__custom_types__).includes(type)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static Customs = __custom_types__
|
|
173
|
+
}
|
|
174
|
+
export class Debug {
|
|
175
|
+
static log(...args) {
|
|
176
|
+
console.log(`[SLIM]`, ...args)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function type(obj, properties = {}) {
|
|
181
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
182
|
+
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
|
|
183
|
+
|
|
184
|
+
const isArray = (obj) => {
|
|
185
|
+
return typeof obj === "object" && Array.isArray(obj)
|
|
186
|
+
}
|
|
187
|
+
const isTypedArray = (arr, t) => {
|
|
188
|
+
return arr.every(element => type(element) === t)
|
|
189
|
+
}
|
|
190
|
+
const isTypedOfArray = (arr, t) => {
|
|
191
|
+
return arr.every(element => typeof element === t)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (obj === null) return "null"
|
|
195
|
+
if (obj === undefined) return "undefined"
|
|
196
|
+
if (Number.isNaN(obj)) return "NaN"
|
|
197
|
+
|
|
198
|
+
// The optional DOM layer requires structural element detection.
|
|
199
|
+
if (obj && typeof obj === "object" && obj.nodeType === 1) return "element"
|
|
200
|
+
if (obj && obj.nodeType === 11) return "fragment"
|
|
201
|
+
|
|
202
|
+
if(typeof obj == "function" && obj.__type__ == true) return "type"
|
|
203
|
+
|
|
204
|
+
if (typeof obj == "object" && obj instanceof SlimVariableType) {
|
|
205
|
+
return obj.kind
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (typeof obj == "object" && !Array.isArray(obj) && "type" in obj && obj.type == Struct) {
|
|
209
|
+
return "struct"
|
|
210
|
+
}
|
|
211
|
+
if (typeof obj == "object" && !Array.isArray(obj) && "type" in obj && obj.type instanceof Enum) {
|
|
212
|
+
return "enum"
|
|
213
|
+
}
|
|
214
|
+
if (typeof obj == "object" && !Array.isArray(obj) && obj instanceof EnumValue) {
|
|
215
|
+
return "enum"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (typeof obj === "function" && obj.__component__ == true) return "component"
|
|
219
|
+
|
|
220
|
+
if (isArray(obj) && isTypedArray(obj, "undefined")) return "null[]"
|
|
221
|
+
if (isArray(obj) && isTypedArray(obj, "string")) return "string[]"
|
|
222
|
+
if (isArray(obj) && isTypedArray(obj, "int")) return "int[]"
|
|
223
|
+
if (isArray(obj) && isTypedArray(obj, "float")) return "float[]"
|
|
224
|
+
if (isArray(obj) && isTypedArray(obj, "object")) return "object[]"
|
|
225
|
+
if (isArray(obj) && isTypedOfArray(obj, "number")) return "number[]"
|
|
226
|
+
if (isArray(obj) && isTypedArray(obj, "enum")) return "enum[]"
|
|
227
|
+
if (isArray(obj) && isTypedArray(obj, "struct")) return "struct[]"
|
|
228
|
+
if (isArray(obj) && isTypedArray(obj, "array")) return "array[]"
|
|
229
|
+
|
|
230
|
+
if (isArray(obj)) return "array"
|
|
231
|
+
|
|
232
|
+
if (obj instanceof Map) return "map"
|
|
233
|
+
if (obj instanceof Set) return "set"
|
|
234
|
+
if (obj instanceof Date) return "date"
|
|
235
|
+
if (obj instanceof Promise) return "promise"
|
|
236
|
+
if (obj instanceof RegExp) return "regexp"
|
|
237
|
+
if (obj instanceof Error) return "error"
|
|
238
|
+
|
|
239
|
+
if (typeof obj === "object" && !Array.isArray(obj)) return "object"
|
|
240
|
+
|
|
241
|
+
if (typeof obj === "string") return "string"
|
|
242
|
+
if (typeof obj === "number" && Number.isInteger(obj)) return "int"
|
|
243
|
+
if (typeof obj === "number" && !Number.isInteger(obj)) return "float"
|
|
244
|
+
|
|
245
|
+
if (typeof obj === "boolean") return "bool"
|
|
246
|
+
if (typeof obj === "bigint") return "bigint"
|
|
247
|
+
if (typeof obj === "symbol") return "symbol"
|
|
248
|
+
|
|
249
|
+
if (typeof obj === "function" && /^\s*class\s+/.test(obj.toString())) return "class"
|
|
250
|
+
if (typeof obj == "function") return "function"
|
|
251
|
+
|
|
252
|
+
return undefined
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
export function __type_ref__(label, resolve = null) {
|
|
257
|
+
return { __slim_type_ref__: true, label, resolve }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function __type_spec__(...references) {
|
|
261
|
+
return references
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function __type_spec_all__(...references) {
|
|
265
|
+
return { __slim_all__: true, references }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function referenceLabel(reference) {
|
|
269
|
+
return typeof reference === "string" ? reference : reference?.label ?? String(reference)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function __type_label__(specification) {
|
|
273
|
+
if (specification && specification.__slim_all__) {
|
|
274
|
+
return specification.references.map(referenceLabel).join(" & ")
|
|
275
|
+
}
|
|
276
|
+
const refs = Array.isArray(specification) ? specification : [specification]
|
|
277
|
+
return refs.map(referenceLabel).join(" | ")
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function resolveTypeReference(reference) {
|
|
281
|
+
if (reference && typeof reference === "object" && reference.__slim_type_ref__) {
|
|
282
|
+
if (typeof reference.resolve === "function") return reference.resolve()
|
|
283
|
+
|
|
284
|
+
const name = reference.label.replace(/(?:\[\])+$/, "").split("::")[0]
|
|
285
|
+
return __custom_types__[name] ?? __structs__[name] ?? __enums__[name]
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (typeof reference === "string") {
|
|
289
|
+
const name = reference.replace(/(?:\[\])+$/, "").split("::")[0]
|
|
290
|
+
return __custom_types__[name] ?? __structs__[name] ?? __enums__[name]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return reference
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function isClass(value) {
|
|
297
|
+
return typeof value === "function" && /^\s*class\s+/.test(Function.prototype.toString.call(value))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function matchesDefinition(definition, value) {
|
|
301
|
+
if (Array.isArray(definition)) return __type_matches__(definition, value)
|
|
302
|
+
|
|
303
|
+
if (typeof definition === "function") {
|
|
304
|
+
if (isClass(definition)) return value instanceof definition
|
|
305
|
+
return definition(value) === true
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return isSameType(definition, value)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function matchesResolvedType(resolved, label, value) {
|
|
312
|
+
if (resolved === undefined || resolved === null) return false
|
|
313
|
+
|
|
314
|
+
if (resolved?.type === Struct) {
|
|
315
|
+
return __typed__(value, resolved, "errorResult").success
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (resolved?.type instanceof Enum) return resolved.has(value)
|
|
319
|
+
if (resolved instanceof EnumValue) return resolved === value
|
|
320
|
+
|
|
321
|
+
if (typeof resolved === "function" && resolved.__type__ === true) {
|
|
322
|
+
return resolved(value) === true
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return matchesDefinition(resolved, value)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const BUILTIN_CLASSES = { Map, Set, Date, Promise, RegExp, Error }
|
|
329
|
+
|
|
330
|
+
// Split tuple and generic members only at top-level commas.
|
|
331
|
+
function splitTypeList(text) {
|
|
332
|
+
const parts = []
|
|
333
|
+
let depth = 0
|
|
334
|
+
let current = ""
|
|
335
|
+
|
|
336
|
+
for (const char of text) {
|
|
337
|
+
if (char === "<" || char === "[" || char === "(") depth++
|
|
338
|
+
else if (char === ">" || char === "]" || char === ")") depth--
|
|
339
|
+
else if (char === "," && depth === 0) { parts.push(current.trim()); current = ""; continue }
|
|
340
|
+
current += char
|
|
341
|
+
}
|
|
342
|
+
if (current.trim()) parts.push(current.trim())
|
|
343
|
+
return parts
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function matchesLabel(label, value) {
|
|
347
|
+
return __type_matches__(__type_spec__(__type_ref__(label)), value)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function matchesNamedType(label, resolved, value) {
|
|
351
|
+
if (label === "any") return true
|
|
352
|
+
|
|
353
|
+
if (label.length > 2 && label.startsWith("[") && label.endsWith("]")) {
|
|
354
|
+
if (!Array.isArray(value)) return false
|
|
355
|
+
|
|
356
|
+
const parts = splitTypeList(label.slice(1, -1))
|
|
357
|
+
if (value.length !== parts.length) return false
|
|
358
|
+
return parts.every((part, index) => matchesLabel(part, value[index]))
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const generic = label.indexOf("<")
|
|
362
|
+
if (generic !== -1 && label.endsWith(">")) {
|
|
363
|
+
const container = label.slice(0, generic).trim()
|
|
364
|
+
const args = splitTypeList(label.slice(generic + 1, -1))
|
|
365
|
+
const first = args[0] ?? "any"
|
|
366
|
+
|
|
367
|
+
if (container === "Array") {
|
|
368
|
+
return Array.isArray(value) && value.every(item => matchesLabel(first, item))
|
|
369
|
+
}
|
|
370
|
+
if (container === "Set") {
|
|
371
|
+
return value instanceof Set && [...value].every(item => matchesLabel(first, item))
|
|
372
|
+
}
|
|
373
|
+
if (container === "Map") {
|
|
374
|
+
const second = args[1] ?? "any"
|
|
375
|
+
return value instanceof Map && [...value].every(([key, entry]) =>
|
|
376
|
+
matchesLabel(first, key) && matchesLabel(second, entry))
|
|
377
|
+
}
|
|
378
|
+
if (container === "Promise") return value instanceof Promise
|
|
379
|
+
|
|
380
|
+
return matchesNamedType(container, resolved, value)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (label.endsWith("[]")) {
|
|
384
|
+
if (!Array.isArray(value)) return false
|
|
385
|
+
|
|
386
|
+
const elementLabel = label.slice(0, -2)
|
|
387
|
+
if (value.length === 0) return type(value) === label || elementLabel === "any"
|
|
388
|
+
|
|
389
|
+
if (resolved !== undefined) {
|
|
390
|
+
return value.every(item => matchesResolvedType(resolved, elementLabel, item))
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return type(value) === label
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const enumSeparator = label.indexOf("::")
|
|
397
|
+
if (enumSeparator !== -1) {
|
|
398
|
+
const enumName = label.slice(0, enumSeparator)
|
|
399
|
+
const member = label.slice(enumSeparator + 2)
|
|
400
|
+
const enumDef = resolved ?? __enums__[enumName]
|
|
401
|
+
return !!enumDef && enumDef[member] === value
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (resolved !== undefined) return matchesResolvedType(resolved, label, value)
|
|
405
|
+
if (label === "number") return type(value) === "int" || type(value) === "float"
|
|
406
|
+
|
|
407
|
+
if (label in BUILTIN_CLASSES) return value instanceof BUILTIN_CLASSES[label]
|
|
408
|
+
|
|
409
|
+
return type(value) === label
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function matchesReference(reference, value) {
|
|
413
|
+
const label = typeof reference === "string" ? reference : reference?.label
|
|
414
|
+
if (!label) return false
|
|
415
|
+
return matchesNamedType(label, resolveTypeReference(reference), value)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function __type_matches__(specification, value) {
|
|
419
|
+
if (specification && specification.__slim_all__) {
|
|
420
|
+
return specification.references.every(reference => matchesReference(reference, value))
|
|
421
|
+
}
|
|
422
|
+
const references = Array.isArray(specification) ? specification : [specification]
|
|
423
|
+
return references.some(reference => matchesReference(reference, value))
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function previewValue(value) {
|
|
427
|
+
try {
|
|
428
|
+
if (typeof value === "string") return JSON.stringify(value)
|
|
429
|
+
if (typeof value === "function") return type(value)
|
|
430
|
+
const text = JSON.stringify(value)
|
|
431
|
+
if (text === undefined) return String(value)
|
|
432
|
+
return text.length > 40 ? text.slice(0, 40) + "…" : text
|
|
433
|
+
} catch {
|
|
434
|
+
return String(value)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function typeError(varName, expected, value, action = "assigned") {
|
|
439
|
+
const actual = type(value)
|
|
440
|
+
const preview = previewValue(value)
|
|
441
|
+
if (action === "redefined") {
|
|
442
|
+
throw new TypeError(`The variable "${varName}" is of type ${expected}, but it was redefined with type ${actual}: ${preview}`)
|
|
443
|
+
}
|
|
444
|
+
if (action === "mutated") {
|
|
445
|
+
throw new TypeError(`The variable "${varName}" is of type ${expected}, but it was mutated into type ${actual}: ${preview}`)
|
|
446
|
+
}
|
|
447
|
+
throw new TypeError(`The "${varName}" is of type ${expected}, but was assigned a ${actual}: ${preview}`)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const guardedValues = new WeakMap()
|
|
451
|
+
const scopedVariableTypes = new WeakMap()
|
|
452
|
+
const typedStaticFields = new WeakMap()
|
|
453
|
+
|
|
454
|
+
function setVariableType(bindingId, definition) {
|
|
455
|
+
if (bindingId && (typeof bindingId === "object" || typeof bindingId === "function")) {
|
|
456
|
+
scopedVariableTypes.set(bindingId, definition)
|
|
457
|
+
} else {
|
|
458
|
+
SlimVariableTypes[bindingId] = definition
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function getVariableType(bindingId) {
|
|
463
|
+
if (bindingId && (typeof bindingId === "object" || typeof bindingId === "function")) {
|
|
464
|
+
return scopedVariableTypes.get(bindingId)
|
|
465
|
+
}
|
|
466
|
+
return SlimVariableTypes[bindingId]
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function setStaticFieldType(owner, field, definition) {
|
|
470
|
+
const fields = typedStaticFields.get(owner) ?? new Map()
|
|
471
|
+
fields.set(field, definition)
|
|
472
|
+
typedStaticFields.set(owner, fields)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function getStaticFieldType(owner, field) {
|
|
476
|
+
return typedStaticFields.get(owner)?.get(field)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function guardTypedValue(value, specification, varName) {
|
|
480
|
+
if (!value || typeof value !== "object" || !__type_matches__(specification, value)) return value
|
|
481
|
+
if (__type_label__(specification) === "any") return value
|
|
482
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value
|
|
483
|
+
|
|
484
|
+
const cached = guardedValues.get(value)
|
|
485
|
+
if (cached?.has(specification)) return cached.get(specification)
|
|
486
|
+
|
|
487
|
+
const restore = (target, property, descriptor) => {
|
|
488
|
+
if (descriptor) Reflect.defineProperty(target, property, descriptor)
|
|
489
|
+
else Reflect.deleteProperty(target, property)
|
|
490
|
+
}
|
|
491
|
+
const check = (target, property, descriptor) => {
|
|
492
|
+
if (__type_matches__(specification, target)) return
|
|
493
|
+
restore(target, property, descriptor)
|
|
494
|
+
typeError(varName, __type_label__(specification), target, "mutated")
|
|
495
|
+
}
|
|
496
|
+
const proxy = new Proxy(value, {
|
|
497
|
+
set(target, property, next) {
|
|
498
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, property)
|
|
499
|
+
const succeeded = Reflect.set(target, property, next, target)
|
|
500
|
+
if (!succeeded) return false
|
|
501
|
+
check(target, property, descriptor)
|
|
502
|
+
return true
|
|
503
|
+
},
|
|
504
|
+
deleteProperty(target, property) {
|
|
505
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, property)
|
|
506
|
+
const succeeded = Reflect.deleteProperty(target, property)
|
|
507
|
+
if (!succeeded) return false
|
|
508
|
+
check(target, property, descriptor)
|
|
509
|
+
return true
|
|
510
|
+
},
|
|
511
|
+
defineProperty(target, property, descriptor) {
|
|
512
|
+
const previous = Object.getOwnPropertyDescriptor(target, property)
|
|
513
|
+
const succeeded = Reflect.defineProperty(target, property, descriptor)
|
|
514
|
+
if (!succeeded) return false
|
|
515
|
+
check(target, property, previous)
|
|
516
|
+
return true
|
|
517
|
+
}
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
const entries = cached ?? new Map()
|
|
521
|
+
entries.set(specification, proxy)
|
|
522
|
+
guardedValues.set(value, entries)
|
|
523
|
+
return proxy
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
export function __def_struct__(name, schema, specifications = {}, defaults = {}, extendsName = null, methods = {}) {
|
|
528
|
+
const parent = extendsName ? __structs__[extendsName] : null
|
|
529
|
+
if (extendsName && !parent) throw new StructError(`Struct "${name}" extends unknown struct "${extendsName}"`)
|
|
530
|
+
|
|
531
|
+
const schemeArray = parent ? { ...parent.scheme } : {}
|
|
532
|
+
const mergedDefaults = parent ? { ...parent.__defaults__, ...defaults } : defaults
|
|
533
|
+
const mergedMethods = parent ? { ...parent.__methods__, ...methods } : methods
|
|
534
|
+
|
|
535
|
+
Object.keys(schema).forEach(item => {
|
|
536
|
+
let fieldName = item
|
|
537
|
+
let optional = false
|
|
538
|
+
if (fieldName.startsWith("*")) {
|
|
539
|
+
optional = true
|
|
540
|
+
fieldName = fieldName.slice(1).trim()
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
schemeArray[fieldName] = {
|
|
544
|
+
type: schema[item],
|
|
545
|
+
specification: specifications[fieldName] ?? schema[item],
|
|
546
|
+
optional
|
|
547
|
+
}
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
const definition = {
|
|
551
|
+
type: Struct,
|
|
552
|
+
name,
|
|
553
|
+
scheme: schemeArray,
|
|
554
|
+
__defaults__: mergedDefaults,
|
|
555
|
+
__methods__: mergedMethods,
|
|
556
|
+
verify: object => __typed__(object, definition),
|
|
557
|
+
verifySafe: object => __typed__(object, definition, "errorResult"),
|
|
558
|
+
new: (values = {}) => {
|
|
559
|
+
const filled = {}
|
|
560
|
+
for (const key of Object.keys(mergedDefaults)) {
|
|
561
|
+
if (!(key in values)) filled[key] = mergedDefaults[key]()
|
|
562
|
+
}
|
|
563
|
+
const data = __typed__({ ...filled, ...values }, definition)
|
|
564
|
+
return Object.keys(mergedMethods).length ? Object.assign(Object.create(mergedMethods), data) : data
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
__structs__[name] = definition
|
|
569
|
+
if (__RESERVED_DEFINES__.has(name)) throw new StructError(`Name "${name}" is reserved`)
|
|
570
|
+
globalThis[name] = definition
|
|
571
|
+
return definition
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function __def_enum__(name, schema) {
|
|
575
|
+
const values = { ...schema }
|
|
576
|
+
const definition = {
|
|
577
|
+
type: new Enum(name),
|
|
578
|
+
name,
|
|
579
|
+
scheme: () => values,
|
|
580
|
+
values: () => Object.values(values),
|
|
581
|
+
keys: () => Object.keys(values),
|
|
582
|
+
has: value => Object.values(values).some(item =>
|
|
583
|
+
value === item || (value instanceof EnumValue && value.value === item)
|
|
584
|
+
)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
Object.keys(values).forEach(key => {
|
|
588
|
+
definition[key] = new EnumValue(values[key])
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
__enums__[name] = definition
|
|
592
|
+
if (__RESERVED_DEFINES__.has(name)) throw new EnumError(`Name "${name}" is reserved`)
|
|
593
|
+
globalThis[name] = definition
|
|
594
|
+
return definition
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function __typed_variable__(value, specification, bindingId, varName = bindingId) {
|
|
598
|
+
if (RELEASE) return value
|
|
599
|
+
const expected = __type_label__(specification)
|
|
600
|
+
if (!__type_matches__(specification, value)) typeError(varName, expected, value)
|
|
601
|
+
|
|
602
|
+
setVariableType(bindingId, {
|
|
603
|
+
expected: specification,
|
|
604
|
+
label: expected,
|
|
605
|
+
name: varName
|
|
606
|
+
})
|
|
607
|
+
return guardTypedValue(value, specification, varName)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export function __typed_return__(value, specification, fnName) {
|
|
611
|
+
if (RELEASE) return value
|
|
612
|
+
|
|
613
|
+
if (!__type_matches__(specification, value)) {
|
|
614
|
+
throw new TypeError(
|
|
615
|
+
`function "${fnName}" must return ${__type_label__(specification)}, got ${type(value)}: ${previewValue(value)}`
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
return value
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function __typed_pattern__(value, typeLabel) {
|
|
622
|
+
if (RELEASE) return value
|
|
623
|
+
if (!__type_matches__(typeLabel, value)) {
|
|
624
|
+
throw new TypeError(`destructured value does not match type ${typeLabel}: ${previewValue(value)}`)
|
|
625
|
+
}
|
|
626
|
+
return value
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export function __typed_variable_check__(bindingId, value, varName = bindingId) {
|
|
630
|
+
if (RELEASE) return value
|
|
631
|
+
const typed = getVariableType(bindingId)
|
|
632
|
+
if (!typed) return value
|
|
633
|
+
if (!__type_matches__(typed.expected, value)) typeError(varName, typed.label, value, "redefined")
|
|
634
|
+
return guardTypedValue(value, typed.expected, varName)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
export function __typed_parameter__(value, specification, bindingId, varName, optional, message) {
|
|
638
|
+
if (RELEASE) return value
|
|
639
|
+
if (optional && (value === undefined || value === null)) {
|
|
640
|
+
setVariableType(bindingId, {
|
|
641
|
+
expected: specification,
|
|
642
|
+
label: __type_label__(specification),
|
|
643
|
+
name: varName
|
|
644
|
+
})
|
|
645
|
+
return value
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (!__type_matches__(specification, value)) {
|
|
649
|
+
throw new ArgumentDeclarationTypeError(message, { skipUserFrames: 1 })
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
setVariableType(bindingId, {
|
|
653
|
+
expected: specification,
|
|
654
|
+
label: __type_label__(specification),
|
|
655
|
+
name: varName
|
|
656
|
+
})
|
|
657
|
+
return guardTypedValue(value, specification, varName)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
export function __typed_static_field__(owner, field, value, specification, displayName) {
|
|
661
|
+
if (RELEASE) return value
|
|
662
|
+
const label = __type_label__(specification)
|
|
663
|
+
if (!__type_matches__(specification, value)) typeError(displayName, label, value)
|
|
664
|
+
setStaticFieldType(owner, field, { expected: specification, label })
|
|
665
|
+
return guardTypedValue(value, specification, displayName)
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export function __typed_static_field_check__(owner, field, value, displayName) {
|
|
669
|
+
if (RELEASE) return value
|
|
670
|
+
const definition = getStaticFieldType(owner, field)
|
|
671
|
+
if (!definition) return value
|
|
672
|
+
if (!__type_matches__(definition.expected, value)) {
|
|
673
|
+
typeError(displayName, definition.label, value, "redefined")
|
|
674
|
+
}
|
|
675
|
+
return guardTypedValue(value, definition.expected, displayName)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export function __argument_typed__(value, expectedType) {
|
|
679
|
+
return __type_matches__(expectedType, value)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export function __type_def__(name, definition, properties = {}) {
|
|
683
|
+
const inherited = properties.extends ?? null
|
|
684
|
+
const customType = value => {
|
|
685
|
+
if (inherited && !__type_matches__(inherited, value)) return false
|
|
686
|
+
return matchesDefinition(definition, value)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
customType.__type__ = true
|
|
690
|
+
customType.__slim_type_name__ = name
|
|
691
|
+
customType.value = definition
|
|
692
|
+
__custom_types__[name] = customType
|
|
693
|
+
return customType
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
697
|
+
__type_def__("email", value => typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
|
|
698
|
+
__type_def__("url", value => typeof value === "string" && /^https?:\/\/[^\s]+$/.test(value))
|
|
699
|
+
__type_def__("uuid", value => typeof value === "string" && UUID.test(value))
|
|
700
|
+
__type_def__("positive", value => typeof value === "number" && value > 0)
|
|
701
|
+
__type_def__("negative", value => typeof value === "number" && value < 0)
|
|
702
|
+
__type_def__("natural", value => Number.isInteger(value) && value >= 0)
|
|
703
|
+
__type_def__("nonempty", value => (typeof value === "string" || Array.isArray(value)) && value.length > 0)
|
|
704
|
+
|
|
705
|
+
export function __typed__(value, structName, returnMethod = "default") {
|
|
706
|
+
const name = typeof structName === "string" ? structName : structName?.name
|
|
707
|
+
const structDef = typeof structName === "string" ? __structs__[structName] : structName
|
|
708
|
+
if (!structDef) throw new StructError(`Unknown struct "${structName}"`)
|
|
709
|
+
|
|
710
|
+
const fail = (error, properties = {}) => {
|
|
711
|
+
if (returnMethod === "errorResult") {
|
|
712
|
+
return { success: false, result: { type: error.tag, msg: String(error) }, properties }
|
|
713
|
+
}
|
|
714
|
+
throw error
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
718
|
+
return fail(new StructError(`Expected object for "${name}"`))
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const schema = structDef.scheme
|
|
722
|
+
const extra = Object.keys(value).filter(key => !(key in schema))
|
|
723
|
+
if (extra.length > 0) {
|
|
724
|
+
return fail(new StructPassedError(
|
|
725
|
+
`"${name}" does not contain any keys named "${extra.join(", ")}", but it receives them`
|
|
726
|
+
))
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
for (const [field, definition] of Object.entries(schema)) {
|
|
730
|
+
if (!(field in value)) {
|
|
731
|
+
if (!definition.optional) {
|
|
732
|
+
return fail(new StructExpectError(
|
|
733
|
+
`"${name}" is receiving fewer keys than expected. The key "${field}" have not been received`
|
|
734
|
+
))
|
|
735
|
+
}
|
|
736
|
+
continue
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (!__type_matches__(definition.specification, value[field])) {
|
|
740
|
+
return fail(new StructError(
|
|
741
|
+
`"${name}.${field}" expected ${__type_label__(definition.specification)}, got ${type(value[field])}: ${previewValue(value[field])}`
|
|
742
|
+
), {
|
|
743
|
+
name: name,
|
|
744
|
+
field: field,
|
|
745
|
+
expected: __type_label__(definition.specification),
|
|
746
|
+
got: type(value[field]),
|
|
747
|
+
fieldName: `${name}.${field}`
|
|
748
|
+
})
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return returnMethod === "errorResult"
|
|
753
|
+
? { success: true, result: value }
|
|
754
|
+
: value
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export function __sizeof__(value) {
|
|
758
|
+
if (value === null || value === undefined) return 0
|
|
759
|
+
if (typeof value === "string") return value.length
|
|
760
|
+
if (Array.isArray(value)) return value.length
|
|
761
|
+
if (typeof value === "object") return Object.keys(value).length
|
|
762
|
+
if (typeof value === "number") return value.toString().length
|
|
763
|
+
if (typeof value === "boolean") return 1
|
|
764
|
+
return 0
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export function __is_empty__(obj) {
|
|
768
|
+
if (__sizeof__(obj) == 0) {
|
|
769
|
+
return true
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
return false
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function __copyof__(obj) {
|
|
777
|
+
if (Type.isObj(obj)) {
|
|
778
|
+
return { ...obj }
|
|
779
|
+
}
|
|
780
|
+
else if (Type.isAnyArray(obj)) {
|
|
781
|
+
const arrCopy = []
|
|
782
|
+
obj.forEach(item => arrCopy.push(item))
|
|
783
|
+
return arrCopy
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
throw TypeError(`Copy target must be object or array, not ${type(obj)}`)
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export function __intdiv__(a, b) {
|
|
791
|
+
return Math.trunc(a / b)
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export function __match_eq__(subject, pattern) {
|
|
795
|
+
const a = subject instanceof EnumValue ? subject.value : subject
|
|
796
|
+
const b = pattern instanceof EnumValue ? pattern.value : pattern
|
|
797
|
+
return a === b
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const IMMUTABLE = new WeakMap()
|
|
801
|
+
|
|
802
|
+
export function __lock_object__(obj) {
|
|
803
|
+
if (obj === null || typeof obj !== "object") return obj
|
|
804
|
+
|
|
805
|
+
if (IMMUTABLE.has(obj)) return IMMUTABLE.get(obj)
|
|
806
|
+
|
|
807
|
+
const handler = {
|
|
808
|
+
set() {
|
|
809
|
+
throw new Error("locked object mutation")
|
|
810
|
+
},
|
|
811
|
+
deleteProperty() {
|
|
812
|
+
throw new Error("locked object mutation")
|
|
813
|
+
},
|
|
814
|
+
defineProperty() {
|
|
815
|
+
throw new Error("locked object mutation")
|
|
816
|
+
},
|
|
817
|
+
setPrototypeOf() {
|
|
818
|
+
throw new Error("locked object mutation")
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const wrapped = new Proxy(obj, handler)
|
|
823
|
+
|
|
824
|
+
IMMUTABLE.set(obj, wrapped)
|
|
825
|
+
|
|
826
|
+
const keys = Reflect.ownKeys(obj)
|
|
827
|
+
|
|
828
|
+
for (const key of keys) {
|
|
829
|
+
const value = obj[key]
|
|
830
|
+
|
|
831
|
+
if (value && typeof value === "object") {
|
|
832
|
+
obj[key] = __lock_object__(value)
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
Object.freeze(obj)
|
|
837
|
+
|
|
838
|
+
return wrapped
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
Object.assign(globalThis, {
|
|
842
|
+
log, warn, error, info, debug,
|
|
843
|
+
|
|
844
|
+
type,
|
|
845
|
+
|
|
846
|
+
Component, Type, Struct,
|
|
847
|
+
|
|
848
|
+
onError, test, assert, assertEqual,
|
|
849
|
+
|
|
850
|
+
__def_struct__, __def_enum__, __typed__, __handle_async_error__,
|
|
851
|
+
__handle_sync_error__, __sizeof__, __is_empty__, __lock_object__,
|
|
852
|
+
__typed_variable__, __typed_variable_check__, __typed_pattern__, __typed_return__,
|
|
853
|
+
__intdiv__, __copyof__, __match_eq__,
|
|
854
|
+
__typed_parameter__, __typed_static_field__, __typed_static_field_check__,
|
|
855
|
+
__type_def__, __argument_typed__,
|
|
856
|
+
__type_ref__, __type_spec__, __type_spec_all__, __type_matches__, __type_label__,
|
|
857
|
+
|
|
858
|
+
StructError, StructPassedError, StructExpectError, ArgumentDeclarationTypeError,
|
|
859
|
+
|
|
860
|
+
PI
|
|
861
|
+
})
|