@thoughtpivot/flight 1.0.7 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/flight.js +59 -2
- package/package.json +1 -1
- package/src/flight.ts +84 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Flight
|
|
2
2
|
|
|
3
|
-
Flight is a modern, high-performance web application server built on Node.js, designed for building scalable, component-driven applications. It
|
|
3
|
+
Flight is a modern, high-performance web application server built on Node.js, designed for building scalable, component-driven applications. It is the successor to [Avian](https://github.com/ispyhumanfly/avian): originally created by ThoughtPivot, handed to FlyPaper Technologies, LLC, where Avian matured—Flight builds on those advancements with modern tooling and developer experience.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -29,6 +29,10 @@ yarn add @thoughtpivot/flight
|
|
|
29
29
|
|
|
30
30
|
Previously published on npm as `@spytech/flight`; switch your dependency to `@thoughtpivot/flight`.
|
|
31
31
|
|
|
32
|
+
### Downstream apps (tsx / patch-package)
|
|
33
|
+
|
|
34
|
+
From **v1.0.8** onward, Flight normalizes `require('yargs/yargs')` when loaders such as **tsx** expose it as `{ default: factory }` instead of the factory. If you only added **patch-package** for that workaround, upgrade `@thoughtpivot/flight`, delete `patches/@thoughtpivot+flight+*.patch`, and remove any **postinstall** hook that existed solely to apply it.
|
|
35
|
+
|
|
32
36
|
## Quick Start
|
|
33
37
|
|
|
34
38
|
1. Create a new project directory and initialize:
|
|
@@ -93,6 +97,7 @@ node flight.js --mode production --app_home .
|
|
|
93
97
|
Available CLI options:
|
|
94
98
|
|
|
95
99
|
- `--app_home`: Application root directory (default: current directory)
|
|
100
|
+
- `--exclude_paths` / `--exclude-paths`: Directories under `app_home` to omit when discovering `**/*.backend.ts` routes. Repeat the flag (`--exclude_paths node_modules --exclude_paths dist`) or use commas (`--exclude_paths node_modules,dist`). Also accepts `FLIGHT_EXCLUDE_PATHS` (comma-separated). Paths must stay inside `app_home`.
|
|
96
101
|
- `--app_key`: Application key for sessions (default: 'flightApp')
|
|
97
102
|
- `--app_secret`: Secret key for session encryption (default: 'the best secret key in the world')
|
|
98
103
|
- `--mode`: 'development' or 'production' (default: 'production')
|
|
@@ -259,8 +264,10 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
259
264
|
|
|
260
265
|
## Acknowledgments
|
|
261
266
|
|
|
262
|
-
Flight
|
|
267
|
+
Flight succeeds the [Avian](https://github.com/ispyhumanfly/avian) framework. Avian began at ThoughtPivot and was later handed to FlyPaper Technologies, LLC, where it grew into the platform many teams relied on. Flight is born from that lineage—the ideas and hardening FlyPaper contributed to Avian—carried forward as ThoughtPivot’s next-generation server.
|
|
268
|
+
|
|
269
|
+
We’re grateful to FlyPaper Technologies for stewarding Avian and advancing component-driven architecture.
|
|
263
270
|
|
|
264
271
|
## ThoughtPivot
|
|
265
272
|
|
|
266
|
-
Flight is
|
|
273
|
+
Today Flight is an official ThoughtPivot technology: developed and maintained by the [ThoughtPivot](https://github.com/thoughtpivot) organization as part of its ecosystem.
|
package/dist/flight.js
CHANGED
|
@@ -21,7 +21,54 @@ const path_1 = __importDefault(require("path"));
|
|
|
21
21
|
const koa_ratelimit_1 = __importDefault(require("koa-ratelimit"));
|
|
22
22
|
const koa_static_1 = __importDefault(require("koa-static"));
|
|
23
23
|
const koa_session_1 = __importDefault(require("koa-session"));
|
|
24
|
-
|
|
24
|
+
function normalizeExcludePaths(value) {
|
|
25
|
+
if (value == null || value === '')
|
|
26
|
+
return [];
|
|
27
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
28
|
+
const out = [];
|
|
29
|
+
for (const p of parts) {
|
|
30
|
+
const s = String(p).trim();
|
|
31
|
+
if (!s)
|
|
32
|
+
continue;
|
|
33
|
+
out.push(...s
|
|
34
|
+
.split(',')
|
|
35
|
+
.map((x) => x.trim())
|
|
36
|
+
.filter(Boolean));
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
function dedupeStrings(items) {
|
|
41
|
+
return [...new Set(items)];
|
|
42
|
+
}
|
|
43
|
+
/** Build fast-glob ignore globs for trees rooted under `appRootAbs`. */
|
|
44
|
+
function backendDiscoveryIgnorePatterns(appRootAbs, excludeRelativeDirs) {
|
|
45
|
+
const patterns = [];
|
|
46
|
+
for (const raw of excludeRelativeDirs) {
|
|
47
|
+
const trimmed = raw.trim();
|
|
48
|
+
if (!trimmed)
|
|
49
|
+
continue;
|
|
50
|
+
const resolved = path_1.default.resolve(appRootAbs, trimmed);
|
|
51
|
+
const rel = path_1.default.relative(appRootAbs, resolved);
|
|
52
|
+
const relPosix = rel.replace(/\\/g, '/');
|
|
53
|
+
if (!relPosix || relPosix.startsWith('..') || path_1.default.isAbsolute(rel)) {
|
|
54
|
+
console.warn(`Flight: exclude_paths entry skipped (outside app_home): ${trimmed}`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
patterns.push(`${relPosix}/**`);
|
|
58
|
+
}
|
|
59
|
+
return patterns;
|
|
60
|
+
}
|
|
61
|
+
const _yargs = require('yargs/yargs');
|
|
62
|
+
const yargsEntry = typeof _yargs === 'function' ? _yargs : _yargs.default;
|
|
63
|
+
const argv = yargsEntry(process.argv.slice(2))
|
|
64
|
+
.option('exclude_paths', {
|
|
65
|
+
alias: 'exclude-paths',
|
|
66
|
+
type: 'array',
|
|
67
|
+
string: true,
|
|
68
|
+
default: [],
|
|
69
|
+
describe: 'Directories under app_home to skip when discovering **/*.backend.ts (repeat flag or comma-separated)'
|
|
70
|
+
})
|
|
71
|
+
.parseSync();
|
|
25
72
|
// Set default session duration (24 hours in milliseconds)
|
|
26
73
|
const DEFAULT_SESSION_DURATION = 86400000; // 24 hours in milliseconds
|
|
27
74
|
// Get session duration from environment variable or command line argument
|
|
@@ -63,8 +110,16 @@ if (argv.disable_vite === undefined) {
|
|
|
63
110
|
// Ensure the value is a boolean
|
|
64
111
|
argv.disable_vite = Boolean(argv.disable_vite);
|
|
65
112
|
const appHomePath = path_1.default.resolve(argv.app_home);
|
|
113
|
+
const excludePathsConfigured = dedupeStrings([
|
|
114
|
+
...normalizeExcludePaths(argv.exclude_paths),
|
|
115
|
+
...normalizeExcludePaths(process.env.FLIGHT_EXCLUDE_PATHS)
|
|
116
|
+
]);
|
|
117
|
+
const backendDiscoveryIgnores = backendDiscoveryIgnorePatterns(appHomePath, excludePathsConfigured);
|
|
66
118
|
process.chdir(appHomePath);
|
|
67
119
|
console.log(appHomePath);
|
|
120
|
+
if (backendDiscoveryIgnores.length > 0) {
|
|
121
|
+
console.log('Flight: excluding **/*.backend.ts discovery under:', backendDiscoveryIgnores.map((g) => g.replace(/\*\*$/, '')).join(', '));
|
|
122
|
+
}
|
|
68
123
|
const mode = process.env.FLIGHT_MODE || argv.mode || 'production';
|
|
69
124
|
/** Prefix without breaking printf-style logs (koa-logger uses `%s` as the first argument). */
|
|
70
125
|
const flightModeLabel = `Flight (${mode}):`;
|
|
@@ -113,7 +168,9 @@ else {
|
|
|
113
168
|
app.use((0, cors_1.default)()).use((0, koa_bodyparser_1.default)({
|
|
114
169
|
jsonLimit: argv.payload_limit
|
|
115
170
|
}));
|
|
116
|
-
const backEndFiles = fast_glob_1.default.sync('**/*.backend.ts'
|
|
171
|
+
const backEndFiles = fast_glob_1.default.sync('**/*.backend.ts', {
|
|
172
|
+
ignore: backendDiscoveryIgnores
|
|
173
|
+
});
|
|
117
174
|
backEndFiles.forEach((file) => {
|
|
118
175
|
const serverRoutes = require(path_1.default.resolve(file));
|
|
119
176
|
console.log('Found component backend file: ' + path_1.default.resolve(file));
|
package/package.json
CHANGED
package/src/flight.ts
CHANGED
|
@@ -19,7 +19,75 @@ import ratelimit from 'koa-ratelimit'
|
|
|
19
19
|
import serve from 'koa-static'
|
|
20
20
|
import session from 'koa-session'
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/** CLI argv shape after Flight applies defaults (see block below). */
|
|
23
|
+
interface FlightArgv {
|
|
24
|
+
session_duration?: number
|
|
25
|
+
app_home?: string
|
|
26
|
+
app_key?: string
|
|
27
|
+
app_secret?: string
|
|
28
|
+
port?: number | string
|
|
29
|
+
payload_limit?: string
|
|
30
|
+
disable_vite?: boolean
|
|
31
|
+
mode?: string
|
|
32
|
+
exclude_paths?: string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeExcludePaths(value: unknown): string[] {
|
|
36
|
+
if (value == null || value === '') return []
|
|
37
|
+
const parts = Array.isArray(value) ? value : [value]
|
|
38
|
+
const out: string[] = []
|
|
39
|
+
for (const p of parts) {
|
|
40
|
+
const s = String(p).trim()
|
|
41
|
+
if (!s) continue
|
|
42
|
+
out.push(
|
|
43
|
+
...s
|
|
44
|
+
.split(',')
|
|
45
|
+
.map((x) => x.trim())
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
return out
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function dedupeStrings(items: string[]): string[] {
|
|
53
|
+
return [...new Set(items)]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Build fast-glob ignore globs for trees rooted under `appRootAbs`. */
|
|
57
|
+
function backendDiscoveryIgnorePatterns(appRootAbs: string, excludeRelativeDirs: string[]): string[] {
|
|
58
|
+
const patterns: string[] = []
|
|
59
|
+
for (const raw of excludeRelativeDirs) {
|
|
60
|
+
const trimmed = raw.trim()
|
|
61
|
+
if (!trimmed) continue
|
|
62
|
+
const resolved = path.resolve(appRootAbs, trimmed)
|
|
63
|
+
const rel = path.relative(appRootAbs, resolved)
|
|
64
|
+
const relPosix = rel.replace(/\\/g, '/')
|
|
65
|
+
if (!relPosix || relPosix.startsWith('..') || path.isAbsolute(rel)) {
|
|
66
|
+
console.warn(`Flight: exclude_paths entry skipped (outside app_home): ${trimmed}`)
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
patterns.push(`${relPosix}/**`)
|
|
70
|
+
}
|
|
71
|
+
return patterns
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// tsx (and similar loaders) may expose require('yargs/yargs') as { default: factory } instead of factory.
|
|
75
|
+
interface YargsInstance {
|
|
76
|
+
option(key: string, opts: Record<string, unknown>): YargsInstance
|
|
77
|
+
parseSync(): FlightArgv
|
|
78
|
+
}
|
|
79
|
+
type YargsFn = (args: string[]) => YargsInstance
|
|
80
|
+
const _yargs = require('yargs/yargs') as YargsFn | { default: YargsFn }
|
|
81
|
+
const yargsEntry: YargsFn = typeof _yargs === 'function' ? _yargs : _yargs.default
|
|
82
|
+
const argv = yargsEntry(process.argv.slice(2))
|
|
83
|
+
.option('exclude_paths', {
|
|
84
|
+
alias: 'exclude-paths',
|
|
85
|
+
type: 'array',
|
|
86
|
+
string: true,
|
|
87
|
+
default: [],
|
|
88
|
+
describe: 'Directories under app_home to skip when discovering **/*.backend.ts (repeat flag or comma-separated)'
|
|
89
|
+
})
|
|
90
|
+
.parseSync() as FlightArgv
|
|
23
91
|
|
|
24
92
|
// Set default session duration (24 hours in milliseconds)
|
|
25
93
|
const DEFAULT_SESSION_DURATION = 86400000 // 24 hours in milliseconds
|
|
@@ -74,9 +142,21 @@ if (argv.disable_vite === undefined) {
|
|
|
74
142
|
argv.disable_vite = Boolean(argv.disable_vite)
|
|
75
143
|
|
|
76
144
|
const appHomePath = path.resolve(argv.app_home)
|
|
145
|
+
const excludePathsConfigured = dedupeStrings([
|
|
146
|
+
...normalizeExcludePaths(argv.exclude_paths),
|
|
147
|
+
...normalizeExcludePaths(process.env.FLIGHT_EXCLUDE_PATHS)
|
|
148
|
+
])
|
|
149
|
+
const backendDiscoveryIgnores = backendDiscoveryIgnorePatterns(appHomePath, excludePathsConfigured)
|
|
150
|
+
|
|
77
151
|
process.chdir(appHomePath)
|
|
78
152
|
|
|
79
153
|
console.log(appHomePath)
|
|
154
|
+
if (backendDiscoveryIgnores.length > 0) {
|
|
155
|
+
console.log(
|
|
156
|
+
'Flight: excluding **/*.backend.ts discovery under:',
|
|
157
|
+
backendDiscoveryIgnores.map((g) => g.replace(/\*\*$/, '')).join(', ')
|
|
158
|
+
)
|
|
159
|
+
}
|
|
80
160
|
|
|
81
161
|
const mode = process.env.FLIGHT_MODE || argv.mode || 'production'
|
|
82
162
|
|
|
@@ -136,7 +216,9 @@ if (cluster.isPrimary) {
|
|
|
136
216
|
})
|
|
137
217
|
)
|
|
138
218
|
|
|
139
|
-
const backEndFiles = fg.sync('**/*.backend.ts'
|
|
219
|
+
const backEndFiles = fg.sync('**/*.backend.ts', {
|
|
220
|
+
ignore: backendDiscoveryIgnores
|
|
221
|
+
})
|
|
140
222
|
backEndFiles.forEach((file) => {
|
|
141
223
|
const serverRoutes = require(path.resolve(file))
|
|
142
224
|
|