Vendor Cordis framework packages as source
cordis 4.0.0-rc.6, plugin-loader, -include, -group, -timer, -hmr, -logger-console, cosmokit 1.8.1, schemastery 3.18.0 — copied from the cordis-workspace checkout, flattened under vendor/, original npm names, private: true. vendor/README.md is the manifest: upstream repos + commit SHAs, local-modification log, sync procedure. Local modification: hmr's locale YAML imports and .i18n() call removed (avoids a runtime YAML import hook we don't vendor).
This commit is contained in:
21
vendor/include/LICENSE
vendored
Normal file
21
vendor/include/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Shigma
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
43
vendor/include/README.md
vendored
Normal file
43
vendor/include/README.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# @cordisjs/plugin-include
|
||||
|
||||
File-backed loader tree for Cordis. The include plugin reads a YAML or JSON
|
||||
file, turns it into loader entries, and writes updates back when the file is
|
||||
writable.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { Context } from 'cordis'
|
||||
import Loader from '@cordisjs/plugin-loader'
|
||||
import Include from '@cordisjs/plugin-include'
|
||||
|
||||
const root = new Context()
|
||||
await root.plugin(Loader, { baseUrl: import.meta.url })
|
||||
await root.plugin(Include, {
|
||||
path: './cordis.yml',
|
||||
initial: [],
|
||||
enableLogs: true,
|
||||
})
|
||||
```
|
||||
|
||||
Example `cordis.yml`:
|
||||
|
||||
```yaml
|
||||
- id: timer
|
||||
name: '@cordisjs/plugin-timer'
|
||||
- id: app
|
||||
name: ./plugins/app
|
||||
config:
|
||||
message: hello
|
||||
```
|
||||
|
||||
## Config
|
||||
|
||||
| Field | Description |
|
||||
| --- | --- |
|
||||
| `path` | YAML or JSON file path resolved from `ctx.baseUrl`. |
|
||||
| `initial` | Entry list written when the file is missing. |
|
||||
| `patches` | Runtime patches applied after reading the file. |
|
||||
| `enableLogs` | Enables loader apply, reload, and unload logs. |
|
||||
|
||||
Patches can insert entries or override fields on entries with a matching `id`.
|
||||
31
vendor/include/package.json
vendored
Normal file
31
vendor/include/package.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@cordisjs/plugin-include",
|
||||
"description": "Include files in cordis configurations",
|
||||
"version": "1.0.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"author": "Shigma <shigma10826@gmail.com>",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@cordisjs/plugin-loader": "^1.0.0-rc.4",
|
||||
"cordis": "^4.0.0-rc.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"cosmokit": "^1.8.1",
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
}
|
||||
229
vendor/include/src/index.ts
vendored
Normal file
229
vendor/include/src/index.ts
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
import { EntryOptions, EntryTree, isJsExpr } from '@cordisjs/plugin-loader'
|
||||
import { Context, Service } from 'cordis'
|
||||
import { extname } from 'node:path'
|
||||
import { access, constants, readFile, rename, writeFile } from 'node:fs/promises'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import * as yaml from 'js-yaml'
|
||||
|
||||
const JsExpr = new yaml.Type('tag:yaml.org,2002:js', {
|
||||
kind: 'scalar',
|
||||
resolve: (data) => typeof data === 'string',
|
||||
construct: (data) => ({ __jsExpr: data }),
|
||||
predicate: isJsExpr,
|
||||
represent: (data) => data['__jsExpr'],
|
||||
})
|
||||
|
||||
const schema = yaml.JSON_SCHEMA.extend(JsExpr)
|
||||
|
||||
const writable: Record<string, string> = {
|
||||
'.json': 'application/json',
|
||||
'.yaml': 'application/yaml',
|
||||
'.yml': 'application/yaml',
|
||||
}
|
||||
|
||||
const supported = new Set(Object.keys(writable))
|
||||
|
||||
/** Runtime patch applied to entries loaded from an included config file. */
|
||||
export interface PatchOptions {
|
||||
id?: string
|
||||
insert?: EntryOptions[]
|
||||
name?: string
|
||||
config?: any
|
||||
group?: boolean | null
|
||||
disabled?: boolean | null
|
||||
inject?: any
|
||||
intercept?: any
|
||||
isolate?: any
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/** Config namespace for the file-backed include loader. */
|
||||
export namespace Include {
|
||||
/** Config for a file-backed loader subtree. */
|
||||
export interface Config {
|
||||
/** YAML or JSON path resolved from `ctx.baseUrl`. */
|
||||
path: string
|
||||
/** Entry list written when the file does not already exist. */
|
||||
initial?: any[]
|
||||
/** Runtime patches applied after reading the file. */
|
||||
patches?: PatchOptions[]
|
||||
/** Enables loader apply/reload/unload logs for this subtree. */
|
||||
enableLogs?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/** Loader entry tree backed by a YAML or JSON file. */
|
||||
export class Include extends EntryTree {
|
||||
static inject = ['loader']
|
||||
|
||||
public filename: string
|
||||
private type?: string
|
||||
private readonly: boolean
|
||||
private content?: string
|
||||
private data?: EntryOptions[]
|
||||
private writeTask?: NodeJS.Timeout
|
||||
|
||||
constructor(ctx: Context, public config: Include.Config) {
|
||||
super(ctx)
|
||||
this.enableLogs = config.enableLogs ?? ctx.fiber.entry?.parent.tree.enableLogs ?? false
|
||||
this.filename = fileURLToPath(new URL(this.config.path, this.ctx.baseUrl))
|
||||
const ext = extname(this.filename)
|
||||
if (!supported.has(ext)) {
|
||||
throw new Error(`extension "${ext}" not supported`)
|
||||
}
|
||||
this.type = writable[ext]
|
||||
this.readonly = !this.type
|
||||
this.ctx.baseUrl = new URL('.', pathToFileURL(this.filename)).href
|
||||
|
||||
ctx.on('internal/update', (config, _, next) => {
|
||||
if (config.path !== this.config.path) return next()
|
||||
this.root.update(this.data!)
|
||||
})
|
||||
}
|
||||
|
||||
private async checkAccess() {
|
||||
if (!this.type) return
|
||||
try {
|
||||
await access(this.filename, constants.W_OK)
|
||||
} catch {
|
||||
this.readonly = true
|
||||
}
|
||||
}
|
||||
|
||||
private async read(forced = false) {
|
||||
const content = await readFile(this.filename, 'utf8')
|
||||
if (!forced && this.content === content) return false
|
||||
this.content = content
|
||||
if (this.type === 'application/yaml') {
|
||||
this.data = yaml.load(this.content, { schema }) as any
|
||||
} else if (this.type === 'application/json') {
|
||||
this.data = JSON.parse(this.content) as any
|
||||
} else {
|
||||
const module = await import(/* @vite-ignore */ this.filename)
|
||||
this.data = module.default || module
|
||||
}
|
||||
await this.checkAccess()
|
||||
return true
|
||||
}
|
||||
|
||||
private applyPatches(data: EntryOptions[]): EntryOptions[] {
|
||||
const { patches } = this.config
|
||||
if (!patches?.length) return data
|
||||
|
||||
const entryMap = new Map<string, EntryOptions>()
|
||||
const buildMap = (entries: EntryOptions[]) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.id) entryMap.set(entry.id, entry)
|
||||
if (entry.group && Array.isArray(entry.config)) {
|
||||
buildMap(entry.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
buildMap(data)
|
||||
|
||||
for (const patch of patches) {
|
||||
const { id, insert, name, ...overrides } = patch
|
||||
|
||||
if (insert) {
|
||||
if (id) {
|
||||
const target = entryMap.get(id)
|
||||
if (!target) {
|
||||
this.ctx.root.logger?.('loader').warn('patch insert: entry %C not found', id)
|
||||
continue
|
||||
}
|
||||
if (!target.group) {
|
||||
this.ctx.root.logger?.('loader').warn('patch insert: entry %C is not a group', id)
|
||||
continue
|
||||
}
|
||||
if (!Array.isArray(target.config)) target.config = []
|
||||
target.config.push(...insert)
|
||||
} else {
|
||||
data.push(...insert)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
this.ctx.root.logger?.('loader').warn('patch: id is required for non-insert patches')
|
||||
continue
|
||||
}
|
||||
|
||||
const target = entryMap.get(id)
|
||||
if (!target) {
|
||||
this.ctx.root.logger?.('loader').warn('patch: entry %C not found', id)
|
||||
continue
|
||||
}
|
||||
|
||||
if (name && name !== target.name) {
|
||||
this.ctx.root.logger?.('loader').warn(
|
||||
'patch: name mismatch for %C (expected %C, got %C), skipping',
|
||||
id, target.name, name,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(overrides)) {
|
||||
if (key === 'id') continue
|
||||
target[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
async* [Service.init]() {
|
||||
try {
|
||||
await this.read()
|
||||
} catch {
|
||||
if (this.config.initial) {
|
||||
this.writeFile(this.config.initial as any)
|
||||
await this.read()
|
||||
} else {
|
||||
throw new Error(`config file not found: ${this.filename}`)
|
||||
}
|
||||
}
|
||||
|
||||
yield () => this.stop()
|
||||
const data = this.applyPatches([...this.data!])
|
||||
await this.root.update(data)
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.root.stop()
|
||||
}
|
||||
|
||||
/** Re-read the file and refresh child entries when content changed. */
|
||||
async refresh() {
|
||||
if (!await this.read()) return
|
||||
this.root.update(this.data!)
|
||||
}
|
||||
|
||||
private async _writeFile(config: EntryOptions[]) {
|
||||
if (this.readonly) {
|
||||
throw new Error(`cannot overwrite readonly config`)
|
||||
}
|
||||
if (this.type === 'application/yaml') {
|
||||
this.content = yaml.dump(config, { schema })
|
||||
} else if (this.type === 'application/json') {
|
||||
this.content = JSON.stringify(config, null, 2)
|
||||
}
|
||||
await writeFile(this.filename + '.tmp', this.content!)
|
||||
await rename(this.filename + '.tmp', this.filename)
|
||||
}
|
||||
|
||||
private writeFile(config: EntryOptions[]) {
|
||||
clearTimeout(this.writeTask)
|
||||
this.writeTask = setTimeout(() => {
|
||||
this.writeTask = undefined
|
||||
this._writeFile(config)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
/** Schedule a write of the current root entry data. */
|
||||
write() {
|
||||
this.context.emit('loader/config-update')
|
||||
return this.writeFile(this.root.data)
|
||||
}
|
||||
}
|
||||
|
||||
export default Include
|
||||
13
vendor/include/tsconfig.json
vendored
Normal file
13
vendor/include/tsconfig.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../cosmokit" },
|
||||
{ "path": "../cordis" },
|
||||
{ "path": "../loader" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user