oox 0.2.0 → 0.3.0-beta10

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.
Files changed (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +32 -29
  3. package/app.js +143 -0
  4. package/bin/argv.js +70 -95
  5. package/bin/cli.js +49 -58
  6. package/bin/configurer.js +64 -78
  7. package/bin/loader.mjs +449 -0
  8. package/bin/proxyer.js +66 -95
  9. package/bin/register.js +55 -105
  10. package/bin/starter.js +71 -144
  11. package/index.js +168 -6
  12. package/index.mjs +5 -0
  13. package/logger.js +40 -0
  14. package/modules/http/index.js +192 -0
  15. package/modules/http/utils.js +73 -0
  16. package/modules/index.js +88 -0
  17. package/modules/module.js +16 -0
  18. package/modules/socketio/client.js +101 -0
  19. package/modules/socketio/index.js +168 -0
  20. package/modules/socketio/server.js +136 -0
  21. package/modules/socketio/socket.js +4 -0
  22. package/package.json +39 -16
  23. package/types/app.d.ts +51 -0
  24. package/types/bin/argv.d.ts +8 -0
  25. package/types/bin/cli.d.ts +4 -0
  26. package/types/bin/configurer.d.ts +3 -0
  27. package/types/bin/proxyer.d.ts +1 -0
  28. package/types/bin/register.d.ts +1 -0
  29. package/types/bin/starter.d.ts +3 -0
  30. package/types/index.d.ts +76 -0
  31. package/types/logger.d.ts +4 -0
  32. package/types/modules/http/index.d.ts +47 -0
  33. package/types/modules/http/utils.d.ts +17 -0
  34. package/types/modules/index.d.ts +24 -0
  35. package/types/modules/module.d.ts +13 -0
  36. package/types/modules/socketio/client.d.ts +23 -0
  37. package/types/modules/socketio/index.d.ts +37 -0
  38. package/types/modules/socketio/server.d.ts +35 -0
  39. package/types/modules/socketio/socket.d.ts +11 -0
  40. package/types/utils.d.ts +6 -0
  41. package/utils.js +63 -0
  42. package/.gitattributes +0 -2
  43. package/global.js +0 -118
  44. package/middleware.js +0 -167
  45. package/rpc/config.class.js +0 -48
  46. package/rpc/context.class.js +0 -15
  47. package/rpc/http.class.js +0 -312
  48. package/rpc/rpc.class.js +0 -231
  49. package/rpc/rpc.interface.class.js +0 -119
  50. package/rpc/socketio.class.js +0 -223
  51. package/service/service.class.js +0 -74
  52. package/service/socketio.class.js +0 -145
  53. package/setMap.class.js +0 -67
  54. package/socketio/client.class.js +0 -190
  55. package/socketio/server.class.js +0 -222
  56. package/socketio/socket.class.js +0 -23
  57. package/util.js +0 -224
package/bin/configurer.js CHANGED
@@ -1,78 +1,64 @@
1
-
2
- const fs = require ( 'fs' )
3
-
4
- const path = require ( 'path' )
5
-
6
- const argv = require ( './argv' )
7
-
8
-
9
-
10
- /**
11
- * x.y=1 => { x: { y: 1 } }
12
- * @param {*} env
13
- */
14
- function mergeFlatEnv ( env ) {
15
-
16
- for ( const key of Object.keys ( env ) ) {
17
-
18
- // x.y.z
19
- if ( key.includes ( '.' ) && !key.startsWith ( '.' ) && !key.endsWith ( '.' ) ) {
20
-
21
- const subKeys = key.split ( '.' )
22
-
23
- const valueKey = subKeys.pop ( )
24
-
25
- let tmpEnv = env
26
-
27
- for ( subKey of subKeys ) {
28
-
29
- if ( subKey in tmpEnv ) { } else {
30
-
31
- tmpEnv [ subKey ] = { }
32
- }
33
-
34
- tmpEnv = tmpEnv [ subKey ]
35
- }
36
-
37
- tmpEnv [ valueKey ] = env [ key ]
38
- }
39
- }
40
- }
41
-
42
-
43
-
44
- exports.configure = ( ) => {
45
-
46
- let env = Object.create ( null )
47
-
48
- const defaultEnvPath = argv.getEnvArg ( 'default-env' )
49
-
50
- if ( defaultEnvPath && fs.existsSync ( defaultEnvPath ) ) {
51
-
52
- Object.assign ( env, require ( path.resolve ( defaultEnvPath ) ) )
53
- }
54
-
55
-
56
-
57
- const envPath = argv.getEnvArg ( 'env' )
58
-
59
- if ( envPath && fs.existsSync ( envPath ) ) {
60
-
61
- Object.assign ( env, require ( path.resolve ( envPath ) ) )
62
- }
63
-
64
- Object.assign ( env, argv.getAllEnvArgs ( ) )
65
-
66
- mergeFlatEnv ( env )
67
-
68
- if ( 'string' === typeof env.ignore ) env.ignore = env.ignore.split ( ',' )
69
-
70
- if ( 'string' === typeof env.registry ) env.registry = env.registry.split ( ',' )
71
-
72
- if ( 'string' === typeof env.origin && env.origin.includes ( ',' ) ) {
73
-
74
- env.origin = env.origin.split ( ',' )
75
- }
76
-
77
- return env
78
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configure = void 0;
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+ const argv = require("./argv");
7
+ /**
8
+ * x.y=1 => { x: { y: 1 } }
9
+ */
10
+ function mergeFlatEnv(env) {
11
+ for (const key of Object.keys(env)) {
12
+ // x.y.z
13
+ if (key.includes('.') && !key.startsWith('.') && !key.endsWith('.')) {
14
+ const subKeys = key.split('.');
15
+ const valueKey = subKeys.pop();
16
+ let tmpEnv = env;
17
+ for (const subKey of subKeys) {
18
+ if (subKey in tmpEnv) { }
19
+ else {
20
+ tmpEnv[subKey] = {};
21
+ }
22
+ tmpEnv = tmpEnv[subKey];
23
+ }
24
+ tmpEnv[valueKey] = env[key];
25
+ }
26
+ }
27
+ }
28
+ async function readEnvFile(filePath) {
29
+ let env = {};
30
+ if (filePath && fs.existsSync(filePath)) {
31
+ if (filePath.endsWith('.json')) {
32
+ const raw = fs.readFileSync(filePath, 'utf-8');
33
+ env = JSON.parse(raw);
34
+ }
35
+ else {
36
+ const finalPath = path.resolve(filePath).replace(/\\/g, '/');
37
+ env = await eval(`import('file://${finalPath}')`);
38
+ }
39
+ }
40
+ else {
41
+ throw new Error('Env file not found: ' + filePath);
42
+ }
43
+ return env.default || env;
44
+ }
45
+ async function configure(mergeEnv) {
46
+ const env = Object.create(null);
47
+ const defaultEnvPath = argv.getEnvArg('default-env');
48
+ const targetEnvPath = argv.getEnvArg('env');
49
+ const defaultEnv = defaultEnvPath ? await readEnvFile(defaultEnvPath) : {};
50
+ const targetEnv = targetEnvPath ? await readEnvFile(targetEnvPath) : {};
51
+ Object.assign(env, defaultEnv, targetEnv, argv.getAllEnvArgs());
52
+ mergeFlatEnv(env);
53
+ if ('string' === typeof env.ignore)
54
+ env.ignore = env.ignore.split(',');
55
+ if ('string' === typeof env.registry)
56
+ env.registry = env.registry.split(',');
57
+ if ('string' === typeof env.origin && env.origin.includes(',')) {
58
+ env.origin = env.origin.split(',');
59
+ }
60
+ if (mergeEnv)
61
+ Object.assign(env, mergeEnv);
62
+ return env;
63
+ }
64
+ exports.configure = configure;
package/bin/loader.mjs ADDED
@@ -0,0 +1,449 @@
1
+
2
+ import path from 'node:path'
3
+
4
+ import os from 'node:os'
5
+
6
+ import fs from 'node:fs'
7
+
8
+ import { get as httpGet } from 'node:http'
9
+
10
+ import { get as httpsGet } from 'node:https'
11
+
12
+ import oox from '../index.mjs'
13
+
14
+
15
+ /**
16
+ * NodeJS versions < 16 dose not support loader chain
17
+ */
18
+ const NodeJSBigVersion = +process.versions.node.split('.').shift()
19
+ const useCompatLoader = NodeJSBigVersion < 16
20
+ const compatLoaders = []
21
+ if ( useCompatLoader ) {
22
+ const args = process.execArgv
23
+ let index = -1, size = args.length
24
+ let isLoaderTag = false
25
+ while ( ++index < size ) {
26
+
27
+ const symbol = args [ index ]
28
+
29
+ if ( isLoaderTag ) {
30
+
31
+ isLoaderTag = false
32
+
33
+ if ( 'oox/loader' === symbol ) break
34
+
35
+ compatLoaders.push ( await import ( symbol ) )
36
+ }
37
+
38
+ if ( [ '--loader', '--experimental-loader' ].includes ( symbol ) ) {
39
+
40
+ isLoaderTag = true
41
+ }
42
+ }
43
+
44
+ compatLoaders.reverse ( )
45
+ }
46
+
47
+
48
+
49
+ function generateRPCProxyScript ( name, attributes = [ ] ) {
50
+
51
+ let attrExports = ''
52
+
53
+ for ( const attr of attributes ) {
54
+
55
+ attrExports += `\nexport const ${attr} = proxyer.${attr}\n`
56
+ }
57
+
58
+ const script = `
59
+ import oox from 'oox'
60
+ function RPC_${name} ( ) { }
61
+ function dotCall ( name, action ) {
62
+
63
+ return new Proxy ( RPC_${name}, {
64
+
65
+ get ( target, key ) {
66
+
67
+ return dotCall ( name, action ? action + '.' + key : key )
68
+ },
69
+
70
+ has ( target, key ) { return true },
71
+
72
+ apply ( target, thisArg, args ) {
73
+
74
+ return oox.rpc ( name, action, args )
75
+ }
76
+ } )
77
+ }
78
+ const proxyer = dotCall ( '${name}', '' )
79
+ export default proxyer
80
+ ${attrExports}
81
+ `
82
+
83
+ return script
84
+ }
85
+
86
+
87
+
88
+ function generateRPCProxyURL ( name, attributes ) {
89
+
90
+ let searchText = ''
91
+
92
+ if ( attributes.length ) {
93
+
94
+ const search = new URLSearchParams ( )
95
+
96
+ for ( const attr of attributes ) {
97
+
98
+ search.append ( 'attr', attr )
99
+ }
100
+
101
+ searchText = '?' + search.toString ( )
102
+ }
103
+
104
+ return `oox://rpc/${name}.mjs${searchText}`
105
+ }
106
+
107
+
108
+
109
+ function isWebURL ( url ) {
110
+
111
+ return url && ( url.startsWith ( 'https://' ) || url.startsWith ( 'http://' ) )
112
+ }
113
+
114
+
115
+
116
+ function isFileURL ( url ) {
117
+
118
+ return url && url.startsWith ( 'file://' )
119
+ }
120
+
121
+
122
+
123
+ function isOOXURL ( url ) {
124
+
125
+ return url && url.startsWith ( 'oox://' )
126
+ }
127
+
128
+
129
+
130
+ function pathCorrect ( path ) {
131
+
132
+ return os.platform ( ) === 'win32' && path.startsWith ( '/' ) ? path.replace ( '/', '' ) : path
133
+ }
134
+
135
+
136
+
137
+ /**
138
+ * make ESModule support dynamic <export *> attributes import
139
+ * @param {string} importerSpecifier the import url
140
+ * @param {string} specifier parentURL
141
+ * @returns {string[]}
142
+ */
143
+ function getImportAttributes ( importerSpecifier, specifier ) {
144
+
145
+ const attributes = [ ]
146
+
147
+ const contents = fs.readFileSync ( importerSpecifier, 'utf-8' )
148
+
149
+ // import * as xxx from "xxx"
150
+ const mergeImport = contents.match ( new RegExp ( `import.+\\*\\s*as\\s+(\\w+)\\s+from\\s*["']${specifier}["']` ) )
151
+
152
+ if ( mergeImport ) {
153
+
154
+ const mergeName = mergeImport [ 1 ]
155
+
156
+ const attributesIterator = contents.matchAll ( new RegExp ( `${mergeName}\\s*\\.\\s*(\\w+)`, 'g' ) )
157
+
158
+ for ( const caseItem of attributesIterator ) {
159
+
160
+ attributes.push ( caseItem [ 1 ] )
161
+ }
162
+ }
163
+
164
+ // import { a, b, c } from 'xxx'
165
+ const attributeImport = contents.match ( new RegExp ( `import.+{(.+)}\\s*from\\s*["']${specifier}["']` ) )
166
+
167
+ if ( attributeImport ) {
168
+
169
+ const definedAttributes = attributeImport [ 1 ].split ( ',' ).map ( v => v.trim ( ) ).filter ( v => !v.startsWith ( 'default' ) )
170
+
171
+ attributes.push ( ...definedAttributes )
172
+ }
173
+
174
+ return Array.from ( new Set ( attributes ) )
175
+ }
176
+
177
+
178
+
179
+ /**
180
+ * transform local call => RPC
181
+ * @param {string} originalSpecifier
182
+ * @param {string} specifier
183
+ * @param {string} parentURL
184
+ * @returns
185
+ */
186
+ function ooxRPCImportTransform ( originalSpecifier, specifier, parentURL ) {
187
+
188
+ const { entryFile } = oox.config
189
+
190
+ const groupURL = entryFile.group ? new URL ( '/' + entryFile.group, 'file://' ) : null
191
+
192
+ // OOX RPC Proxy URL generation
193
+ if ( !specifier.endsWith ( entryFile.path ) && groupURL && specifier.startsWith ( groupURL.href ) ) {
194
+
195
+ const subSpecifier = specifier.slice ( groupURL.href.length )
196
+
197
+ const matchResult = subSpecifier.match ( /^\/?([\w-]+)(\/index)?(\.m?js)?$/ )
198
+
199
+ if ( matchResult ) {
200
+
201
+ const importerSpecifier = parentURL.replace ( os.platform ( ) === 'win32' ? /file:\/+/ : 'file://', '' )
202
+
203
+ const attributes = getImportAttributes ( importerSpecifier, originalSpecifier )
204
+
205
+ return { shortCircuit: true, url: generateRPCProxyURL ( matchResult [ 1 ], attributes ) }
206
+ }
207
+ }
208
+
209
+ return null
210
+ }
211
+
212
+
213
+
214
+ /**
215
+ * Directory import supported
216
+ * Ignored '.ts|.js' suffix import supported
217
+ * @param {string} specifier
218
+ * @returns {string}
219
+ */
220
+ function directoryImportTransform ( specifier ) {
221
+
222
+ let filename = new URL ( specifier ).pathname
223
+
224
+ if ( os.platform ( ) === 'win32' && filename.startsWith ( '/' ) ) {
225
+
226
+ filename = filename.replace ( '/', '' )
227
+ }
228
+
229
+ const stat0 = fs.statSync ( filename, {
230
+ throwIfNoEntry: false
231
+ } )
232
+
233
+ if ( !stat0 || stat0.isDirectory ( ) ) {
234
+
235
+ const justNeedEntry = stat0 && stat0.isDirectory ( )
236
+
237
+ const dirname = justNeedEntry ? filename : path.dirname ( filename )
238
+
239
+ const stat1 = justNeedEntry ? stat0 : fs.statSync ( dirname, {
240
+ throwIfNoEntry: false
241
+ } )
242
+
243
+ // find entry file
244
+ if ( stat1 && stat1.isDirectory ( ) ) {
245
+
246
+ const paths = filename.split ( '/' )
247
+
248
+ const filenameWithoutExtension = justNeedEntry ? 'index' : paths.pop ( )
249
+
250
+ const matchEntryRegExp = new RegExp ( `^${filenameWithoutExtension}\\.((\\w?js)|(ts\\w?))$` )
251
+
252
+ const entries = fs.readdirSync ( dirname )
253
+
254
+ for ( const entry of entries ) {
255
+
256
+ if ( !matchEntryRegExp.test ( entry ) ) continue
257
+
258
+ paths.push ( entry )
259
+
260
+ specifier = 'file://' + paths.join ( '/' )
261
+
262
+ break
263
+ }
264
+ }
265
+ }
266
+
267
+ return specifier
268
+ }
269
+
270
+
271
+
272
+ /**
273
+ * @param {string} specifier
274
+ * @param {{
275
+ * conditions: string[],
276
+ * parentURL: string | undefined,
277
+ * }} context
278
+ * @param {Function} defaultResolve
279
+ * @returns {Promise<{ url: string }>}
280
+ */
281
+ export async function resolve ( specifier, context, defaultResolve ) {
282
+
283
+ const originalSpecifier = specifier
284
+
285
+ const { parentURL } = context
286
+
287
+ // HTTP & HTTPS
288
+ if ( isWebURL ( specifier ) ) {
289
+
290
+ return { shortCircuit: true, url: specifier }
291
+ } else if ( isWebURL ( parentURL ) && ( specifier.startsWith ( '.' ) || specifier.startsWith ( '/' ) ) ) {
292
+
293
+ return { shortCircuit: true, url: new URL ( specifier, parentURL ).href }
294
+ }
295
+
296
+ // OOX special alias for web package
297
+ if ( specifier === 'oox' && !isFileURL ( parentURL ) ) {
298
+
299
+ return {
300
+ shortCircuit: true,
301
+ url: await import.meta.resolve ( specifier )
302
+ }
303
+ }
304
+
305
+ if ( !isFileURL ( specifier ) ) {
306
+
307
+ const { url } = defaultResolve ( specifier, context, defaultResolve )
308
+
309
+ specifier = url
310
+ }
311
+
312
+ const ooxRPCTransform = ooxRPCImportTransform ( originalSpecifier, specifier, parentURL )
313
+
314
+ if ( ooxRPCTransform ) return ooxRPCTransform
315
+
316
+ specifier = directoryImportTransform ( specifier )
317
+
318
+ if ( useCompatLoader ) {
319
+
320
+ const iterator = compatLoaders.values ( )
321
+
322
+ const { value } = iterator.next ( )
323
+
324
+ if ( value ) {
325
+
326
+ return value.resolve ( specifier, context, ( specifier, context, nextResolve ) => {
327
+
328
+ const { value } = iterator.next ( )
329
+
330
+ if ( value ) return value.resolve ( specifier, context, nextResolve )
331
+
332
+ return defaultResolve ( specifier, context, defaultResolve )
333
+ } )
334
+ }
335
+ }
336
+
337
+ return defaultResolve ( specifier, context, defaultResolve )
338
+ }
339
+
340
+
341
+
342
+ /**
343
+ * @param {string} url
344
+ * @param {{
345
+ * format: string,
346
+ * }} context If resolve settled with a `format`, that value is included here.
347
+ * @param {Function} defaultLoad
348
+ * @returns {Promise<{
349
+ * format: string,
350
+ * source: string | ArrayBuffer | SharedArrayBuffer | Uint8Array,
351
+ * }>}
352
+ */
353
+ export async function load ( url, context, defaultLoad ) {
354
+
355
+ if ( isWebURL ( url ) || isOOXURL ( url ) ) {
356
+
357
+ return getSource ( url, context, defaultLoad )
358
+ }
359
+
360
+ if ( useCompatLoader ) {
361
+
362
+ const iterator = compatLoaders.values ( )
363
+
364
+ const { value } = iterator.next ( )
365
+
366
+ if ( value ) {
367
+
368
+ return value.load ( url, context, ( url, context, nextLoad ) => {
369
+
370
+ const { value } = iterator.next ( )
371
+
372
+ if ( value ) return value.load ( url, context, nextLoad )
373
+
374
+ return defaultLoad ( url, context, defaultLoad )
375
+ } )
376
+ }
377
+ }
378
+
379
+ return defaultLoad ( url, context, defaultLoad )
380
+ }
381
+
382
+
383
+
384
+ export function getFormat ( url, context, defaultGetFormat ) {
385
+
386
+ if ( isWebURL ( url ) || isOOXURL ( url ) ) {
387
+
388
+ return {
389
+ format: 'module'
390
+ }
391
+ }
392
+
393
+ if ( useCompatLoader ) {
394
+
395
+ const iterator = compatLoaders.values ( )
396
+
397
+ const { value } = iterator.next ( )
398
+
399
+ if ( value ) {
400
+
401
+ return value.getFormat ( url, context, ( url, context, nextGetFormat ) => {
402
+
403
+ const { value } = iterator.next ( )
404
+
405
+ if ( value ) return value.getFormat ( url, context, nextGetFormat )
406
+
407
+ return defaultGetFormat ( url, context, defaultGetFormat )
408
+ } )
409
+ }
410
+ }
411
+
412
+ return defaultGetFormat ( url, context, defaultGetFormat )
413
+ }
414
+
415
+
416
+
417
+ export function getSource ( url, context, defaultGetSource ) {
418
+
419
+ if ( isWebURL ( url ) ) {
420
+
421
+ const getMethod = url.startsWith ( 'https://' ) ? httpsGet : httpGet
422
+
423
+ return new Promise ( ( resolve, reject ) => getMethod ( url, res => {
424
+ let source = ''
425
+ res
426
+ .on ( 'data', chunk => source += chunk )
427
+ .on ( 'end', () => resolve ( { shortCircuit: true, format: 'module', source } ) )
428
+ } ).on ( 'error', reject ) )
429
+ } else if ( isOOXURL ( url ) ) {
430
+
431
+ const mURL = new URL ( url )
432
+
433
+ if ( mURL.host === 'rpc' ) {
434
+
435
+ const regexp = /\/([\w-]+)\.mjs$/
436
+
437
+ const matchResult = mURL.pathname.match ( regexp )
438
+
439
+ // read all import attributes
440
+ const attributes = mURL.searchParams.getAll ( 'attr' )
441
+
442
+ const source = generateRPCProxyScript ( matchResult [ 1 ], attributes )
443
+
444
+ return { shortCircuit: true, format: 'module', source }
445
+ }
446
+ }
447
+
448
+ return defaultGetSource ( url, context, defaultGetSource )
449
+ }