From 9e245b3b045c767677daf9971d146539b420dec7 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Sun, 22 Dec 2024 18:22:44 -0800 Subject: [PATCH 1/4] feat: add time zone support --- .vscode/launch.json | 4 + cspell.json | 6 +- go.work | 1 + runtime/.goreleaser.yaml | 9 +- runtime/Makefile | 4 +- runtime/go.mod | 2 +- runtime/graphql/graphql.go | 13 + runtime/hostfunctions/system.go | 30 + runtime/timezones/timezones.go | 90 + runtime/timezones/tzdata_other.go | 59 + runtime/timezones/tzdata_windows.go | 74 + runtime/utils/context.go | 1 + runtime/wasmhost/hostfns.go | 17 +- runtime/wasmhost/wasmhost.go | 4 + sdk/assemblyscript/examples/time/.prettierrc | 3 + sdk/assemblyscript/examples/time/README.md | 6 + .../examples/time/asconfig.json | 6 + .../examples/time/assembly/index.ts | 38 + .../examples/time/assembly/tsconfig.json | 4 + .../examples/time/eslint.config.js | 11 + sdk/assemblyscript/examples/time/modus.json | 10 + .../examples/time/package-lock.json | 1746 +++++++++++++++++ sdk/assemblyscript/examples/time/package.json | 32 + sdk/assemblyscript/src/assembly/index.ts | 3 + sdk/assemblyscript/src/assembly/localtime.ts | 56 + sdk/go/examples/time/build.cmd | 12 + sdk/go/examples/time/build.sh | 12 + sdk/go/examples/time/go.mod | 5 + sdk/go/examples/time/go.sum | 2 + sdk/go/examples/time/main.go | 114 ++ sdk/go/examples/time/modus.json | 10 + sdk/go/pkg/localtime/imports_mock.go | 42 + sdk/go/pkg/localtime/imports_wasi.go | 66 + sdk/go/pkg/localtime/localtime.go | 63 + sdk/go/pkg/utils/utils.go | 9 + 35 files changed, 2558 insertions(+), 6 deletions(-) create mode 100644 runtime/timezones/timezones.go create mode 100644 runtime/timezones/tzdata_other.go create mode 100644 runtime/timezones/tzdata_windows.go create mode 100644 sdk/assemblyscript/examples/time/.prettierrc create mode 100644 sdk/assemblyscript/examples/time/README.md create mode 100644 sdk/assemblyscript/examples/time/asconfig.json create mode 100644 sdk/assemblyscript/examples/time/assembly/index.ts create mode 100644 sdk/assemblyscript/examples/time/assembly/tsconfig.json create mode 100644 sdk/assemblyscript/examples/time/eslint.config.js create mode 100644 sdk/assemblyscript/examples/time/modus.json create mode 100644 sdk/assemblyscript/examples/time/package-lock.json create mode 100644 sdk/assemblyscript/examples/time/package.json create mode 100644 sdk/assemblyscript/src/assembly/localtime.ts create mode 100644 sdk/go/examples/time/build.cmd create mode 100755 sdk/go/examples/time/build.sh create mode 100644 sdk/go/examples/time/go.mod create mode 100644 sdk/go/examples/time/go.sum create mode 100644 sdk/go/examples/time/main.go create mode 100644 sdk/go/examples/time/modus.json create mode 100644 sdk/go/pkg/localtime/imports_mock.go create mode 100644 sdk/go/pkg/localtime/imports_wasi.go create mode 100644 sdk/go/pkg/localtime/localtime.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 2da8cbb4..3e35788b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -187,6 +187,10 @@ "label": "Text Generation Model Example", "value": "textgeneration" }, + { + "label": "Time Example", + "value": "time" + }, { "label": "Vectors API Example", "value": "vectors" diff --git a/cspell.json b/cspell.json index 39699943..34e767ef 100644 --- a/cspell.json +++ b/cspell.json @@ -24,6 +24,7 @@ "buger", "buildmode", "cconf", + "checklinkname", "chewxy", "classid", "codegen", @@ -97,6 +98,7 @@ "Lessable", "lestrrat", "linkname", + "localtime", "logit", "logits", "logprob", @@ -176,6 +178,7 @@ "tseslint", "tsrv", "typedarray", + "tzif", "uids", "uncategorized", "Unmarshalled", @@ -209,6 +212,7 @@ "xsync", "xxhash", "zenquotes", - "zerolog" + "zerolog", + "zoneinfo" ] } diff --git a/go.work b/go.work index 983a1996..dd7759c6 100644 --- a/go.work +++ b/go.work @@ -19,6 +19,7 @@ use ( ./sdk/go/examples/postgresql ./sdk/go/examples/simple ./sdk/go/examples/textgeneration + ./sdk/go/examples/time ./sdk/go/examples/vectors ./sdk/go/templates/default ) diff --git a/runtime/.goreleaser.yaml b/runtime/.goreleaser.yaml index 4e76ae2e..66306b12 100644 --- a/runtime/.goreleaser.yaml +++ b/runtime/.goreleaser.yaml @@ -23,7 +23,14 @@ builds: - amd64 - arm64 mod_timestamp: "{{ .CommitTimestamp }}" - ldflags: -s -w -X github.com/hypermodeinc/modus/runtime/config.version={{.Version}} + ldflags: + - -s + - -w + - -X github.com/hypermodeinc/modus/runtime/config.version={{.Version}} + - >- + {{- if eq .Os "windows"}} + -checklinkname=0 + {{- end }} # temporarily disabled # notarize: diff --git a/runtime/Makefile b/runtime/Makefile index 588685fe..f56cf130 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -1,5 +1,6 @@ EXECUTABLE := modus_runtime VERSION := $(shell git describe --tags --always --match 'runtime/*' | sed 's/^runtime\///') +LDFLAGS := -s -w -X github.com/hypermodeinc/modus/runtime/config.version=$(VERSION) ifneq ($(OS), Windows_NT) OS := $(shell uname -s) @@ -7,6 +8,7 @@ endif ifeq ($(OS), Windows_NT) EXECUTABLE := $(EXECUTABLE).exe + LDFLAGS := $(LDFLAGS) -checklinkname=0 endif .PHONY: all @@ -23,7 +25,7 @@ build-explorer: .PHONY: build build: build-explorer - go build -o $(EXECUTABLE) -ldflags "-s -w -X github.com/hypermodeinc/modus/runtime/config.version=$(VERSION)" . + go build -o $(EXECUTABLE) -ldflags "$(LDFLAGS)" . .PHONY: run run: build-explorer diff --git a/runtime/go.mod b/runtime/go.mod index 819eb21c..6749fb62 100644 --- a/runtime/go.mod +++ b/runtime/go.mod @@ -48,6 +48,7 @@ require ( github.com/wundergraph/graphql-go-tools/execution v1.1.0 github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.136 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 + golang.org/x/sys v0.28.0 google.golang.org/grpc v1.69.2 ) @@ -131,7 +132,6 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect diff --git a/runtime/graphql/graphql.go b/runtime/graphql/graphql.go index 4d34b009..2d0b685b 100644 --- a/runtime/graphql/graphql.go +++ b/runtime/graphql/graphql.go @@ -21,6 +21,7 @@ import ( "github.com/hypermodeinc/modus/runtime/logger" "github.com/hypermodeinc/modus/runtime/manifestdata" "github.com/hypermodeinc/modus/runtime/pluginmanager" + "github.com/hypermodeinc/modus/runtime/timezones" "github.com/hypermodeinc/modus/runtime/utils" "github.com/hypermodeinc/modus/runtime/wasmhost" @@ -101,6 +102,18 @@ func handleGraphQLRequest(w http.ResponseWriter, r *http.Request) { output := make(map[string]wasmhost.ExecutionInfo) ctx = context.WithValue(ctx, utils.FunctionOutputContextKey, output) + // Set time zone in the context + var timeZone string + if tz := r.Header.Get("X-Time-Zone"); tz != "" { + // If the X-Time-Zone header is set in the request, use that time zone. + timeZone = tz + } else { + // Otherwise, use the host's local time zone. + // Note, the TZ environment variable can be set to override the actual local time zone. + timeZone = timezones.GetLocalTimeZone() + } + ctx = context.WithValue(ctx, utils.TimeZoneContextKey, timeZone) + // Set tracing options var options = []eng.ExecutionOptions{} if utils.TraceModeEnabled() { diff --git a/runtime/hostfunctions/system.go b/runtime/hostfunctions/system.go index 834997e7..8c213aa4 100644 --- a/runtime/hostfunctions/system.go +++ b/runtime/hostfunctions/system.go @@ -13,8 +13,10 @@ import ( "context" "fmt" "os" + "time" "github.com/hypermodeinc/modus/runtime/logger" + "github.com/hypermodeinc/modus/runtime/timezones" "github.com/hypermodeinc/modus/runtime/utils" ) @@ -22,6 +24,8 @@ func init() { const module_name = "modus_system" registerHostFunction(module_name, "logMessage", LogMessage) + registerHostFunction(module_name, "getTimeInZone", GetTimeInZone) + registerHostFunction(module_name, "getTimeZoneData", GetTimeZoneData) } func LogMessage(ctx context.Context, level, message string) { @@ -46,3 +50,29 @@ func LogMessage(ctx context.Context, level, message string) { Bool("user_visible", true). Msg("Message logged from function.") } + +func GetTimeInZone(ctx context.Context, tz *string) *string { + now := time.Now() + + var loc *time.Location + if tz != nil && *tz != "" { + loc = timezones.GetLocation(*tz) + } else if tz, ok := ctx.Value(utils.TimeZoneContextKey).(string); ok { + loc = timezones.GetLocation(tz) + } + + if loc != nil { + now = now.In(loc) + } + + s := now.Format(time.RFC3339Nano) + return &s +} + +func GetTimeZoneData(tz, format *string) []byte { + if tz == nil { + return nil + } + + return timezones.GetTimeZoneData(*tz, *format) +} diff --git a/runtime/timezones/timezones.go b/runtime/timezones/timezones.go new file mode 100644 index 00000000..df778a59 --- /dev/null +++ b/runtime/timezones/timezones.go @@ -0,0 +1,90 @@ +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package timezones + +import ( + "fmt" + "os" + "time" + + "github.com/puzpuzpuz/xsync/v3" +) + +type tzInfo struct { + location *time.Location + data []byte +} + +var systemTimeZone string +var tzCache = *xsync.NewMapOf[string, *tzInfo]() + +func init() { + if tz, err := getSystemLocalTimeZone(); err == nil { + systemTimeZone = tz + } else { + fmt.Fprintf(os.Stderr, "failed to determine system local time zone (using UTC): %v\n", err) + systemTimeZone = "UTC" + } +} + +func GetLocalTimeZone() string { + // check every time, in case the env var has changed via .env file reload + if tz := os.Getenv("TZ"); tz != "" { + return tz + } + + return systemTimeZone +} + +func GetLocation(tz string) *time.Location { + if tz == "" { + return nil + } + + info, err := getTimeZoneInfo(tz) + if err != nil { + return nil + } + + return info.location +} + +func GetTimeZoneData(tz, format string) []byte { + if tz == "" { + return nil + } + + // only support tzif format for now + // we can expand this to support other formats as needed + if format != "tzif" { + return nil + } + + info, err := getTimeZoneInfo(tz) + if err != nil { + return nil + } + + return info.data +} + +func getTimeZoneInfo(tz string) (*tzInfo, error) { + if info, ok := tzCache.Load(tz); ok { + return info, nil + } + + info, err := loadTimeZoneInfo(tz) + if err != nil { + return nil, err + } + + tzCache.Store(tz, info) + return info, nil +} diff --git a/runtime/timezones/tzdata_other.go b/runtime/timezones/tzdata_other.go new file mode 100644 index 00000000..9231cc7a --- /dev/null +++ b/runtime/timezones/tzdata_other.go @@ -0,0 +1,59 @@ +//go:build !windows + +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package timezones + +import ( + "errors" + "fmt" + "os" + "path" + "strings" + "time" +) + +func loadTimeZoneInfo(tz string) (*tzInfo, error) { + tzFile := "/usr/share/zoneinfo/" + tz + if _, err := os.Stat(tzFile); err != nil { + return nil, fmt.Errorf("could not find timezone file: %v", err) + } + + bytes, err := os.ReadFile(tzFile) + if err != nil { + return nil, fmt.Errorf("could not read timezone file: %v", err) + } + + loc, err := time.LoadLocationFromTZData(tz, bytes) + if err != nil { + return nil, fmt.Errorf("could not load timezone data: %v", err) + } + + info := &tzInfo{loc, bytes} + return info, nil +} + +func getSystemLocalTimeZone() (string, error) { + // On Linux and macOS, we use the system default /etc/localtime file to get the time zone. + // It is a symlink to the time zone file within the OS's copy of the IANA time zone database. + // The time zone identifier is the path to the file relative to the zoneinfo folder. + + p, err := os.Readlink("/etc/localtime") + if err == nil { + segments := strings.Split(p, string(os.PathSeparator)) + for i := len(segments) - 1; i >= 0; i-- { + if segments[i] == "zoneinfo" { + return path.Join(segments[i+1:]...), nil + } + } + } + + return "", errors.New("failed to determine system local time zone") +} diff --git a/runtime/timezones/tzdata_windows.go b/runtime/timezones/tzdata_windows.go new file mode 100644 index 00000000..1a3f39bd --- /dev/null +++ b/runtime/timezones/tzdata_windows.go @@ -0,0 +1,74 @@ +//go:build windows + +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package timezones + +import ( + "fmt" + "time" + "unsafe" + + _ "time/tzdata" + _ "unsafe" + + "golang.org/x/sys/windows" +) + +// note: the following link requires -ldflags "-checklinkname=0" on go 1.23 or later + +//go:linkname loadFromEmbeddedTZData time/tzdata.loadFromEmbeddedTZData +func loadFromEmbeddedTZData(string) (string, error) + +func loadTimeZoneInfo(tz string) (*tzInfo, error) { + + var data []byte + if s, err := loadFromEmbeddedTZData(tz); err != nil { + return nil, fmt.Errorf("could not load timezone data: %v", err) + } else { + data = []byte(s) + } + + loc, err := time.LoadLocationFromTZData(tz, data) + if err != nil { + return nil, fmt.Errorf("could not load timezone data: %v", err) + } + + info := &tzInfo{loc, data} + return info, nil +} + +func getSystemLocalTimeZone() (string, error) { + // On Windows, we use the ICU library to get the default time zone in IANA format. + // This requires Windows 10 release 1703 or later. + // See https://learn.microsoft.com/en-us/windows/win32/intl/international-components-for-unicode--icu- + + // We also import time/tzdata to have an embedded copy of the IANA time zone database. + + var errorCode int32 + const bufferSize = 128 + buffer := make([]uint16, bufferSize) + + lib := windows.NewLazySystemDLL("icuin.dll") + proc := lib.NewProc("ucal_getDefaultTimeZone") + + ret, _, err := proc.Call( + uintptr(unsafe.Pointer(&buffer[0])), + uintptr(bufferSize), + uintptr(unsafe.Pointer(&errorCode)), + ) + + if ret == 0 || errorCode != 0 { + return "", fmt.Errorf("failed to determine system local time zone: %v [0x%x]", err, errorCode) + } + + tz := windows.UTF16ToString(buffer) + return tz, nil +} diff --git a/runtime/utils/context.go b/runtime/utils/context.go index 8adcd9a3..86ec2c4c 100644 --- a/runtime/utils/context.go +++ b/runtime/utils/context.go @@ -20,3 +20,4 @@ const FunctionNameContextKey contextKey = "function_name" const FunctionOutputContextKey contextKey = "function_output" const FunctionMessagesContextKey contextKey = "function_messages" const CustomTypesContextKey contextKey = "custom_types" +const TimeZoneContextKey contextKey = "time_zone" diff --git a/runtime/wasmhost/hostfns.go b/runtime/wasmhost/hostfns.go index 411cac00..69e789a3 100644 --- a/runtime/wasmhost/hostfns.go +++ b/runtime/wasmhost/hostfns.go @@ -256,8 +256,18 @@ func (host *wasmHost) newHostFunction(modName, funcName string, fn any, opts ... if hasContextParam { inputs = append(inputs, reflect.ValueOf(ctx)) } - for _, param := range params { - inputs = append(inputs, reflect.ValueOf(param)) + for i, param := range params { + if param == nil { + var rt reflect.Type + if hasContextParam { + rt = rtFunc.In(i + 1) + } else { + rt = rtFunc.In(i) + } + inputs = append(inputs, reflect.New(rt).Elem()) + } else { + inputs = append(inputs, reflect.ValueOf(param)) + } } // Prepare to call the host function @@ -372,6 +382,9 @@ func decodeParams(ctx context.Context, wa langsupport.WasmAdapter, plan langsupp if err != nil { return err } + if data == nil { + continue + } // special case for structs represented as maps switch m := data.(type) { diff --git a/runtime/wasmhost/wasmhost.go b/runtime/wasmhost/wasmhost.go index 3ecb7636..6fbf774c 100644 --- a/runtime/wasmhost/wasmhost.go +++ b/runtime/wasmhost/wasmhost.go @@ -111,6 +111,9 @@ func (host *wasmHost) GetModuleInstance(ctx context.Context, plugin *plugins.Plu wOut := io.MultiWriter(buffers.StdOut(), wInfoLog) wErr := io.MultiWriter(buffers.StdErr(), wErrorLog) + // Get the time zone to pass to the module instance. + tz := ctx.Value(utils.TimeZoneContextKey).(string) + // Configure the module instance. // Note, we use an anonymous module name (empty string) here, // for concurrency and performance reasons. @@ -123,6 +126,7 @@ func (host *wasmHost) GetModuleInstance(ctx context.Context, plugin *plugins.Plu WithSysWalltime().WithSysNanotime(). WithRandSource(rand.Reader). WithStdout(wOut).WithStderr(wErr). + WithEnv("TZ", tz). WithEnv("CLAIMS", jwtClaims) // Instantiate the plugin as a module. diff --git a/sdk/assemblyscript/examples/time/.prettierrc b/sdk/assemblyscript/examples/time/.prettierrc new file mode 100644 index 00000000..64cb35ca --- /dev/null +++ b/sdk/assemblyscript/examples/time/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["assemblyscript-prettier"] +} diff --git a/sdk/assemblyscript/examples/time/README.md b/sdk/assemblyscript/examples/time/README.md new file mode 100644 index 00000000..988c9494 --- /dev/null +++ b/sdk/assemblyscript/examples/time/README.md @@ -0,0 +1,6 @@ +# Modus Simple Example + +This is a _very_ simple example showing how to write a Modus app. +It doesn't do very much, but it should illustrate how to write a function. + +See [./assembly/index.ts](./assembly/index.ts) for the implementation. diff --git a/sdk/assemblyscript/examples/time/asconfig.json b/sdk/assemblyscript/examples/time/asconfig.json new file mode 100644 index 00000000..d8372651 --- /dev/null +++ b/sdk/assemblyscript/examples/time/asconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "./node_modules/@hypermode/modus-sdk-as/plugin.asconfig.json", + "options": { + "transform": ["@hypermode/modus-sdk-as/transform", "json-as/transform"] + } +} diff --git a/sdk/assemblyscript/examples/time/assembly/index.ts b/sdk/assemblyscript/examples/time/assembly/index.ts new file mode 100644 index 00000000..485ec3ab --- /dev/null +++ b/sdk/assemblyscript/examples/time/assembly/index.ts @@ -0,0 +1,38 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +import { localtime } from "@hypermode/modus-sdk-as"; + +/** + * Returns the current UTC time. + */ +export function getUtcTime(): Date { + return new Date(Date.now()); +} + +/** + * Returns the current local time. + */ +export function getLocalTime(): string { + return localtime.Now(); +} + +/** + * Returns the current time in a specified time zone. + */ +export function getTimeInZone(tz: string): string { + return localtime.NowInZone(tz); +} + +/** + * Returns the local time zone identifier. + */ +export function getLocalTimeZone(): string { + return localtime.GetTimeZone(); + + // Alternatively, you can use the following: + // return process.env.get("TZ"); +} diff --git a/sdk/assemblyscript/examples/time/assembly/tsconfig.json b/sdk/assemblyscript/examples/time/assembly/tsconfig.json new file mode 100644 index 00000000..798b474e --- /dev/null +++ b/sdk/assemblyscript/examples/time/assembly/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./**/*.ts"] +} diff --git a/sdk/assemblyscript/examples/time/eslint.config.js b/sdk/assemblyscript/examples/time/eslint.config.js new file mode 100644 index 00000000..7ad50aea --- /dev/null +++ b/sdk/assemblyscript/examples/time/eslint.config.js @@ -0,0 +1,11 @@ +// @ts-check + +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; +import aseslint from "@hypermode/modus-sdk-as/tools/assemblyscript-eslint"; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + aseslint.config, +); diff --git a/sdk/assemblyscript/examples/time/modus.json b/sdk/assemblyscript/examples/time/modus.json new file mode 100644 index 00000000..6f2b9ff9 --- /dev/null +++ b/sdk/assemblyscript/examples/time/modus.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://schema.hypermode.com/modus.json", + "endpoints": { + "default": { + "type": "graphql", + "path": "/graphql", + "auth": "bearer-token" + } + } +} diff --git a/sdk/assemblyscript/examples/time/package-lock.json b/sdk/assemblyscript/examples/time/package-lock.json new file mode 100644 index 00000000..da134dd3 --- /dev/null +++ b/sdk/assemblyscript/examples/time/package-lock.json @@ -0,0 +1,1746 @@ +{ + "name": "time-example", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "time-example", + "license": "Apache-2.0", + "dependencies": { + "@hypermode/modus-sdk-as": "../../src", + "json-as": "^0.9.26" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@types/eslint__js": "^8.42.3", + "assemblyscript": "^0.27.31", + "assemblyscript-prettier": "^3.0.1", + "eslint": "^9.17.0", + "prettier": "^3.4.2", + "typescript": "^5.7.2", + "typescript-eslint": "^8.18.2", + "visitor-as": "^0.11.4" + } + }, + "../../src": { + "name": "@hypermode/modus-sdk-as", + "license": "Apache-2.0", + "dependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "chalk": "^5.3.0", + "json-as": "^0.9.26", + "semver": "^7.6.3", + "xid-ts": "^1.1.4" + }, + "bin": { + "modus-as-build": "bin/build-plugin.js" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@types/eslint__js": "^8.42.3", + "@types/node": "^22.10.2", + "as-test": "^0.3.5", + "assemblyscript": "^0.27.31", + "assemblyscript-prettier": "^3.0.1", + "eslint": "^9.17.0", + "prettier": "^3.4.2", + "typescript": "^5.7.2", + "typescript-eslint": "^8.18.1", + "visitor-as": "^0.11.4" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@hypermode/modus-sdk-as": { + "resolved": "../../src", + "link": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", + "integrity": "sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.18.2", + "@typescript-eslint/type-utils": "8.18.2", + "@typescript-eslint/utils": "8.18.2", + "@typescript-eslint/visitor-keys": "8.18.2", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.2.tgz", + "integrity": "sha512-y7tcq4StgxQD4mDr9+Jb26dZ+HTZ/SkfqpXSiqeUXZHxOUyjWDKsmwKhJ0/tApR08DgOhrFAoAhyB80/p3ViuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.18.2", + "@typescript-eslint/types": "8.18.2", + "@typescript-eslint/typescript-estree": "8.18.2", + "@typescript-eslint/visitor-keys": "8.18.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.2.tgz", + "integrity": "sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.2", + "@typescript-eslint/visitor-keys": "8.18.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz", + "integrity": "sha512-AB/Wr1Lz31bzHfGm/jgbFR0VB0SML/hd2P1yxzKDM48YmP7vbyJNHRExUE/wZsQj2wUCvbWH8poNHFuxLqCTnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.18.2", + "@typescript-eslint/utils": "8.18.2", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.2.tgz", + "integrity": "sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.2.tgz", + "integrity": "sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.2", + "@typescript-eslint/visitor-keys": "8.18.2", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.2.tgz", + "integrity": "sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.2", + "@typescript-eslint/types": "8.18.2", + "@typescript-eslint/typescript-estree": "8.18.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.2.tgz", + "integrity": "sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/as-virtual": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/as-virtual/-/as-virtual-0.2.0.tgz", + "integrity": "sha512-zI+8VwD6BPUtXrxs4R7v2N+59OAIBRwiiv9nGIMwcQ/w/lspyaualMqM7yok3jMRglKqLkfOx+dCJimeflEJcA==", + "license": "MIT" + }, + "node_modules/assemblyscript": { + "version": "0.27.31", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.31.tgz", + "integrity": "sha512-Ra8kiGhgJQGZcBxjtMcyVRxOEJZX64kd+XGpjWzjcjgxWJVv+CAQO0aDBk4GQVhjYbOkATarC83mHjAVGtwPBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "binaryen": "116.0.0-nightly.20240114", + "long": "^5.2.1" + }, + "bin": { + "asc": "bin/asc.js", + "asinit": "bin/asinit.js" + }, + "engines": { + "node": ">=16", + "npm": ">=7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/assemblyscript-prettier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/assemblyscript-prettier/-/assemblyscript-prettier-3.0.1.tgz", + "integrity": "sha512-q5D9rJOr1xG8p2MdcAF3wvIBPrmHaKgenJbmYrwM1T96sHKFeTOfCtgHHmrDnrqoly7wSGfWtbE84omC1IUJEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "assemblyscript": "~0.27.0" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binaryen": { + "version": "116.0.0-nightly.20240114", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz", + "integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "wasm-opt": "bin/wasm-opt", + "wasm2js": "bin/wasm2js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-as": { + "version": "0.9.26", + "resolved": "https://registry.npmjs.org/json-as/-/json-as-0.9.26.tgz", + "integrity": "sha512-0Feb3TtQ1fXTdQWmG699P+46DxoAU/GaRII2/5iiwUo+ufD6hEiBYewxDp4kM3d2nyLwCga8zJOELC7ZA00BLQ==", + "license": "MIT", + "dependencies": { + "as-virtual": "^0.2.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.2.tgz", + "integrity": "sha512-KuXezG6jHkvC3MvizeXgupZzaG5wjhU3yE8E7e6viOvAvD9xAWYp8/vy0WULTGe9DYDWcQu7aW03YIV3mSitrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.18.2", + "@typescript-eslint/parser": "8.18.2", + "@typescript-eslint/utils": "8.18.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/visitor-as": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/visitor-as/-/visitor-as-0.11.4.tgz", + "integrity": "sha512-uih7AooY2V3LhzobjLqyEQzhYYBGeq0y/rZk295foM1Ko498f24NNXBDyM1SgzcLMFCAT/fpmSRco1BpTIdKNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "ts-mixer": "^6.0.2" + }, + "peerDependencies": { + "assemblyscript": "^0.25.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/sdk/assemblyscript/examples/time/package.json b/sdk/assemblyscript/examples/time/package.json new file mode 100644 index 00000000..5e03d198 --- /dev/null +++ b/sdk/assemblyscript/examples/time/package.json @@ -0,0 +1,32 @@ +{ + "name": "time-example", + "private": true, + "description": "Modus AssemblyScript Time Example", + "author": "Hypermode Inc.", + "license": "Apache-2.0", + "type": "module", + "scripts": { + "build": "modus-as-build", + "lint": "eslint .", + "pretty": "prettier --write .", + "pretty:check": "prettier --check ." + }, + "dependencies": { + "@hypermode/modus-sdk-as": "../../src", + "json-as": "^0.9.26" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@types/eslint__js": "^8.42.3", + "assemblyscript": "^0.27.31", + "assemblyscript-prettier": "^3.0.1", + "eslint": "^9.17.0", + "prettier": "^3.4.2", + "typescript": "^5.7.2", + "typescript-eslint": "^8.18.2", + "visitor-as": "^0.11.4" + }, + "overrides": { + "assemblyscript": "$assemblyscript" + } +} diff --git a/sdk/assemblyscript/src/assembly/index.ts b/sdk/assemblyscript/src/assembly/index.ts index 20b2996e..8f83d1f3 100644 --- a/sdk/assemblyscript/src/assembly/index.ts +++ b/sdk/assemblyscript/src/assembly/index.ts @@ -37,4 +37,7 @@ export { neo4j }; import * as utils from "./utils"; export { utils }; +import * as localtime from "./localtime"; +export { localtime }; + export * from "./dynamicmap"; diff --git a/sdk/assemblyscript/src/assembly/localtime.ts b/sdk/assemblyscript/src/assembly/localtime.ts new file mode 100644 index 00000000..20796a96 --- /dev/null +++ b/sdk/assemblyscript/src/assembly/localtime.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +// @ts-expect-error: decorator +@external("modus_system", "getTimeInZone") +declare function hostGetTimeInZone(tz: string | null): string | null; + +// TODO: Find or create a library for AssemblyScript that can handle time zones, +// and then expose modus_system.getTimeZoneData for that library. + +/** + * Returns the current local time, in ISO 8601 extended format. + * + * The time zone is determined in the following order of precedence: + * - If the X-Time-Zone header is present in the request, the time zone is set to the value of the header. + * - If the TZ environment variable is set on the host, the time zone is set to the value of the variable. + * - Otherwise, the time zone is set to the host's local time zone. + */ +export function Now(): string { + const ts = hostGetTimeInZone(null); + if (ts === null) { + throw new Error("Failed to get the local time."); + } + return ts; +} + +/** + * Returns the current local time, in ISO 8601 extended format. + * + * @param tz - A valid IANA time zone identifier, such as "America/New_York" + */ +export function NowInZone(tz: string): string { + if (tz === "") { + throw new Error("A time zone is required."); + } + + const ts = hostGetTimeInZone(tz); + if (ts === null) { + throw new Error("Failed to get the local time."); + } + + return ts; +} + +/** + * Returns the local time zone identifier, in IANA format. + */ +export function GetTimeZone(): string { + return process.env.get("TZ"); +} diff --git a/sdk/go/examples/time/build.cmd b/sdk/go/examples/time/build.cmd new file mode 100644 index 00000000..ed9d2952 --- /dev/null +++ b/sdk/go/examples/time/build.cmd @@ -0,0 +1,12 @@ +@echo off + +:: This build script works best for examples that are in this repository. +:: If you are using this as a template for your own project, you may need to modify this script, +:: to invoke the modus-go-build tool with the correct path to your project. + +SET "PROJECTDIR=%~dp0" +pushd ..\..\tools\modus-go-build > nul +go run . "%PROJECTDIR%" +set "exit_code=%ERRORLEVEL%" +popd > nul +exit /b %exit_code% diff --git a/sdk/go/examples/time/build.sh b/sdk/go/examples/time/build.sh new file mode 100755 index 00000000..02743fec --- /dev/null +++ b/sdk/go/examples/time/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# This build script works best for examples that are in this repository. +# If you are using this as a template for your own project, you may need to modify this script, +# to invoke the modus-go-build tool with the correct path to your project. + +PROJECTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +pushd ../../tools/modus-go-build > /dev/null +go run . "$PROJECTDIR" +exit_code=$? +popd > /dev/null +exit $exit_code diff --git a/sdk/go/examples/time/go.mod b/sdk/go/examples/time/go.mod new file mode 100644 index 00000000..1a3d011c --- /dev/null +++ b/sdk/go/examples/time/go.mod @@ -0,0 +1,5 @@ +module time-example + +go 1.23.4 + +require github.com/hypermodeinc/modus/sdk/go v0.15.0 diff --git a/sdk/go/examples/time/go.sum b/sdk/go/examples/time/go.sum new file mode 100644 index 00000000..76b3dc99 --- /dev/null +++ b/sdk/go/examples/time/go.sum @@ -0,0 +1,2 @@ +github.com/hypermodeinc/modus/sdk/go v0.15.0 h1:xRD4swlxdug4fVVKzY2FQKGUTKK2qLSDb0k/wqDDTOw= +github.com/hypermodeinc/modus/sdk/go v0.15.0/go.mod h1:Tgh2CfZztlmsAi5DAF1s2LhoARF6P61huICuuAd7hvY= diff --git a/sdk/go/examples/time/main.go b/sdk/go/examples/time/main.go new file mode 100644 index 00000000..ff27da86 --- /dev/null +++ b/sdk/go/examples/time/main.go @@ -0,0 +1,114 @@ +/* + * This example is part of the Modus project, licensed under the Apache License 2.0. + * You may modify and use this example in accordance with the license. + * See the LICENSE file that accompanied this code for further details. + */ + +package main + +import ( + "fmt" + "time" + + "github.com/hypermodeinc/modus/sdk/go/pkg/localtime" +) + +/* + * NOTES: + * + * - Typically in Go, `time.Now()` returns the current time in the local time zone. But in Modus (due to Go's WASI implementation), + * it returns the current time in UTC - even though `time.Now().Location()` still returns "Local". + * Thus, to get the local time, you should use the `localtime` package provided by Modus - as shown in the examples below. + * If you actually want UTC, you should prefer `time.Now().UTC()` - which is unambiguous. + * + * - If you return a `time.Time` object to Modus, it will be converted to UTC - regardless of the time zone. + * Thus, if you need to return a time object to Modus, you should return a formatted string instead - Preferably in RFC3339 (ISO 8601) format. + * You can use `time.RFC3339``, or `time.RFC3339Nano`` for more precision, or any other format you prefer. + * + * - The `localTime.GetLocation` function is very similar to `time.LoadLocation` built into Go. For efficiency, `localtime.GetLocation` + * gets time zone data from the Modus runtime - which gets it from the host OS in most cases. However, if you are importing a third-party + * library that expects `time.LoadLocation` to work correctly, you can import Go's `time/tzdata` package, which will embeds the time zone + * database in your wasm binary. This will allow `time.LoadLocation` to work as expected, at the cost of binary size and a slight increase + * in execution time. + */ + +// Returns the current time in UTC. +func GetUtcTime() time.Time { + return time.Now().UTC() +} + +// Returns the current local time. +func GetLocalTime() (string, error) { + now, err := localtime.Now() + if err != nil { + return "", err + } + return now.Format(time.RFC3339), nil +} + +// Returns the current time in a specified time zone. +func GetTimeInZone(tz string) (string, error) { + now, err := localtime.NowInZone(tz) + if err != nil { + return "", err + } + return now.Format(time.RFC3339), nil +} + +// Returns the local time zone identifier. +func GetLocalTimeZone() string { + return localtime.GetTimeZone() + + // Alternatively, you can use the following: + // return os.Getenv("TZ") +} + +type TimeZoneInfo struct { + StandardName string + StandardOffset string + DaylightName string + DaylightOffset string +} + +// Returns some basic information about the time zone specified. +func GetTimeZoneInfo(tz string) (*TimeZoneInfo, error) { + loc, err := localtime.GetLocation(tz) + if err != nil { + return nil, err + } + + janName, janOffset := time.Date(2024, 1, 1, 0, 0, 0, 0, loc).Zone() + julName, julOffset := time.Date(2024, 7, 1, 0, 0, 0, 0, loc).Zone() + + var stdName, dltName string + var stdOffset, dltOffset int + if janOffset <= julOffset { + stdName, stdOffset = janName, janOffset + dltName, dltOffset = julName, julOffset + } else { + stdName, stdOffset = julName, julOffset + dltName, dltOffset = janName, janOffset + } + + info := &TimeZoneInfo{ + StandardName: stdName, + StandardOffset: formatOffset(stdOffset), + DaylightName: dltName, + DaylightOffset: formatOffset(dltOffset), + } + + return info, nil +} + +// Formats the offset in hours and minutes (used by GetTimeZoneInfo). +func formatOffset(offset int) string { + sign := "+" + if offset < 0 { + sign = "-" + offset = -offset + } + offset /= 60 + hours := offset / 60 + minutes := offset % 60 + return fmt.Sprintf("%s%02d:%02d", sign, hours, minutes) +} diff --git a/sdk/go/examples/time/modus.json b/sdk/go/examples/time/modus.json new file mode 100644 index 00000000..6f2b9ff9 --- /dev/null +++ b/sdk/go/examples/time/modus.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://schema.hypermode.com/modus.json", + "endpoints": { + "default": { + "type": "graphql", + "path": "/graphql", + "auth": "bearer-token" + } + } +} diff --git a/sdk/go/pkg/localtime/imports_mock.go b/sdk/go/pkg/localtime/imports_mock.go new file mode 100644 index 00000000..dff6f265 --- /dev/null +++ b/sdk/go/pkg/localtime/imports_mock.go @@ -0,0 +1,42 @@ +//go:build !wasip1 + +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package localtime + +import ( + "time" + + "github.com/hypermodeinc/modus/sdk/go/pkg/testutils" +) + +var GetLocalTimeCallStack = testutils.NewCallStack() +var GetTimeInZoneCallStack = testutils.NewCallStack() +var GetTimeLocationCallStack = testutils.NewCallStack() + +func hostGetLocalTime() (time.Time, error) { + GetLocalTimeCallStack.Push() + return time.Now(), nil +} + +func hostGetTimeInZone(tz string) (time.Time, error) { + GetTimeInZoneCallStack.Push(tz) + loc, err := time.LoadLocation(tz) + if err != nil { + return time.Time{}, err + } + + return time.Now().In(loc), nil +} + +func hostGetTimeLocation(tz string) (*time.Location, error) { + GetTimeLocationCallStack.Push() + return time.LoadLocation(tz) +} diff --git a/sdk/go/pkg/localtime/imports_wasi.go b/sdk/go/pkg/localtime/imports_wasi.go new file mode 100644 index 00000000..40cda200 --- /dev/null +++ b/sdk/go/pkg/localtime/imports_wasi.go @@ -0,0 +1,66 @@ +//go:build wasip1 + +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package localtime + +import ( + "fmt" + "time" + "unsafe" +) + +//go:noescape +//go:wasmimport modus_system getTimeInZone +func _hostGetTimeInZone(tz *string) *string + +func hostGetLocalTime() (time.Time, error) { + ts := _hostGetTimeInZone(nil) + if ts == nil { + return time.Time{}, fmt.Errorf("failed to get the local time") + } + return time.Parse(time.RFC3339Nano, *ts) +} + +func hostGetTimeInZone(tz string) (time.Time, error) { + ts := _hostGetTimeInZone(&tz) + if ts == nil { + return time.Time{}, fmt.Errorf("failed to get time in %s", tz) + } + return time.Parse(time.RFC3339Nano, *ts) +} + +//go:noescape +//go:wasmimport modus_system getTimeZoneData +func _hostGetTimeZoneData(tz, format *string) unsafe.Pointer + +//modus:import modus_system getTimeZoneData +func hostGetTimeZoneData(tz, format *string) *[]byte { + data := _hostGetTimeZoneData(tz, format) + if data == nil { + return nil + } + return (*[]byte)(data) +} + +func hostGetTimeLocation(tz string) (*time.Location, error) { + format := "tzif" + data := hostGetTimeZoneData(&tz, &format) + if data == nil { + return nil, fmt.Errorf("timezone data not found for %s", tz) + } + + loc, err := time.LoadLocationFromTZData(tz, *data) + if err != nil { + return nil, fmt.Errorf("failed to load timezone data for %s: %w", tz, err) + } + + return loc, nil +} diff --git a/sdk/go/pkg/localtime/localtime.go b/sdk/go/pkg/localtime/localtime.go new file mode 100644 index 00000000..331bc382 --- /dev/null +++ b/sdk/go/pkg/localtime/localtime.go @@ -0,0 +1,63 @@ +/* + * Copyright 2024 Hypermode Inc. + * Licensed under the terms of the Apache License, Version 2.0 + * See the LICENSE file that accompanied this code for further details. + * + * SPDX-FileCopyrightText: 2024 Hypermode Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +package localtime + +import ( + "errors" + "os" + "time" +) + +// Now returns the current local time. The time zone is determined in the following order of precedence: +// - If the X-Time-Zone header is present in the request, the time zone is set to the value of the header. +// - If the TZ environment variable is set on the host, the time zone is set to the value of the variable. +// - Otherwise, the time zone is set to the host's local time zone. +func Now() (time.Time, error) { + + // Try to return a time with the proper full time zone. + if tz := os.Getenv("TZ"); tz != "" { + if loc, err := hostGetTimeLocation(tz); err == nil { + return time.Now().In(loc), nil + } + } + + // Otherwise, let the host get the local time with the current offset applied. + return hostGetLocalTime() +} + +// NowInZone returns the current time in the given time zone. +// The time zone should be a valid IANA time zone identifier, such as "America/New_York". +func NowInZone(tz string) (time.Time, error) { + if tz == "" { + return time.Time{}, errors.New("a time zone is required") + } + + // Try to return a time with the proper full time zone. + if loc, err := hostGetTimeLocation(tz); err == nil { + return time.Now().In(loc), nil + } + + // Otherwise, let the host get the local time with the current offset applied. + return hostGetTimeInZone(tz) +} + +// GetTimeZone returns the local time zone identifier, in IANA format. +func GetTimeZone() string { + return os.Getenv("TZ") +} + +// GetLocation returns the time.Location for the given time zone. +// The time zone should be a valid IANA time zone identifier, such as "America/New_York". +func GetLocation(tz string) (*time.Location, error) { + if tz == "" { + return nil, errors.New("time zone is required") + } + return hostGetTimeLocation(tz) +} diff --git a/sdk/go/pkg/utils/utils.go b/sdk/go/pkg/utils/utils.go index d6be3f60..be6bb347 100644 --- a/sdk/go/pkg/utils/utils.go +++ b/sdk/go/pkg/utils/utils.go @@ -15,8 +15,17 @@ import ( "errors" "fmt" "strconv" + "time" ) +func GetLocalTime(tz string) time.Time { + loc, err := time.LoadLocation(tz) + if err != nil { + return time.Now() + } + return time.Now().In(loc) +} + // JsonSerialize serializes the given value to JSON. // Unlike json.Marshal, it does not escape HTML characters. // It also returns results without an extra newline at the end. From fb1d7f90f74a47c2857bc54379e4ee4d6d4f3254 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 23 Dec 2024 18:21:07 -0800 Subject: [PATCH 2/4] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c141530..fb934ec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ - test: add tests for AssemblyScript SDK Transform [#659](https://github.com/hypermodeinc/modus/pull/659) - fix: improve Go version handling [#660](https://github.com/hypermodeinc/modus/pull/660) - fix: update runtime wasm tests [#661](https://github.com/hypermodeinc/modus/pull/661) -- fix: support TinyGo 0.35.0 [#661](https://github.com/hypermodeinc/modus/pull/662) +- fix: support TinyGo 0.35.0 [#662](https://github.com/hypermodeinc/modus/pull/662) +- feat: add APIs for local time and time zones [#663](https://github.com/hypermodeinc/modus/pull/663) ## 2024-12-13 - Runtime 0.15.0 From 67c7371773d73c9e3c3550c51c05ea82eb9352de Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 23 Dec 2024 18:25:36 -0800 Subject: [PATCH 3/4] . --- sdk/go/pkg/utils/utils.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sdk/go/pkg/utils/utils.go b/sdk/go/pkg/utils/utils.go index be6bb347..d6be3f60 100644 --- a/sdk/go/pkg/utils/utils.go +++ b/sdk/go/pkg/utils/utils.go @@ -15,17 +15,8 @@ import ( "errors" "fmt" "strconv" - "time" ) -func GetLocalTime(tz string) time.Time { - loc, err := time.LoadLocation(tz) - if err != nil { - return time.Now() - } - return time.Now().In(loc) -} - // JsonSerialize serializes the given value to JSON. // Unlike json.Marshal, it does not escape HTML characters. // It also returns results without an extra newline at the end. From 0445e132dbe402e06fab0f04c4ac00a3eafe0e59 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 23 Dec 2024 18:37:01 -0800 Subject: [PATCH 4/4] . --- runtime/wasmhost/wasmhost.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/runtime/wasmhost/wasmhost.go b/runtime/wasmhost/wasmhost.go index 6fbf774c..55e8ca78 100644 --- a/runtime/wasmhost/wasmhost.go +++ b/runtime/wasmhost/wasmhost.go @@ -19,6 +19,7 @@ import ( "github.com/hypermodeinc/modus/runtime/logger" "github.com/hypermodeinc/modus/runtime/middleware" "github.com/hypermodeinc/modus/runtime/plugins" + "github.com/hypermodeinc/modus/runtime/timezones" "github.com/hypermodeinc/modus/runtime/utils" "github.com/rs/zerolog" @@ -112,7 +113,12 @@ func (host *wasmHost) GetModuleInstance(ctx context.Context, plugin *plugins.Plu wErr := io.MultiWriter(buffers.StdErr(), wErrorLog) // Get the time zone to pass to the module instance. - tz := ctx.Value(utils.TimeZoneContextKey).(string) + var timeZone string + if tz, ok := ctx.Value(utils.TimeZoneContextKey).(string); ok { + timeZone = tz + } else { + timeZone = timezones.GetLocalTimeZone() + } // Configure the module instance. // Note, we use an anonymous module name (empty string) here, @@ -126,7 +132,7 @@ func (host *wasmHost) GetModuleInstance(ctx context.Context, plugin *plugins.Plu WithSysWalltime().WithSysNanotime(). WithRandSource(rand.Reader). WithStdout(wOut).WithStderr(wErr). - WithEnv("TZ", tz). + WithEnv("TZ", timeZone). WithEnv("CLAIMS", jwtClaims) // Instantiate the plugin as a module.