oox 0.3.0-beta9 → 0.3.1

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 (45) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +29 -32
  3. package/app.js +131 -143
  4. package/bin/argv.js +63 -70
  5. package/bin/cli.js +57 -43
  6. package/bin/configurer.js +60 -62
  7. package/bin/loader.mjs +392 -279
  8. package/bin/proxy-import.js +12 -0
  9. package/bin/proxy-require.js +88 -0
  10. package/bin/register.js +46 -55
  11. package/bin/starter.js +63 -66
  12. package/index.js +155 -168
  13. package/index.mjs +4 -4
  14. package/logger.js +25 -40
  15. package/modules/http/index.js +234 -192
  16. package/modules/http/utils.js +74 -73
  17. package/modules/index.js +86 -88
  18. package/modules/module.js +11 -16
  19. package/modules/socketio/client.js +97 -101
  20. package/modules/socketio/index.js +171 -168
  21. package/modules/socketio/server.js +188 -136
  22. package/modules/socketio/socket.js +1 -4
  23. package/package.json +14 -12
  24. package/types/app.d.ts +50 -51
  25. package/types/bin/argv.d.ts +8 -8
  26. package/types/bin/cli.d.ts +6 -2
  27. package/types/bin/configurer.d.ts +3 -1
  28. package/types/bin/proxy-import.d.ts +4 -0
  29. package/types/bin/proxy-require.d.ts +5 -0
  30. package/types/bin/register.d.ts +1 -1
  31. package/types/bin/starter.d.ts +5 -1
  32. package/types/index.d.ts +78 -76
  33. package/types/logger.d.ts +5 -4
  34. package/types/modules/http/index.d.ts +58 -47
  35. package/types/modules/http/utils.d.ts +14 -17
  36. package/types/modules/index.d.ts +24 -24
  37. package/types/modules/module.d.ts +11 -13
  38. package/types/modules/socketio/client.d.ts +23 -23
  39. package/types/modules/socketio/index.d.ts +37 -37
  40. package/types/modules/socketio/server.d.ts +44 -35
  41. package/types/modules/socketio/socket.d.ts +11 -11
  42. package/types/utils.d.ts +6 -6
  43. package/utils.js +57 -63
  44. package/bin/proxyer.js +0 -61
  45. package/types/bin/proxyer.d.ts +0 -1
package/bin/loader.mjs CHANGED
@@ -1,279 +1,392 @@
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
- function generateRPCProxyScript ( name, attributes = [ ] ) {
17
-
18
- let attrExports = ''
19
-
20
- for ( const attr of attributes ) {
21
-
22
- attrExports += `\nexport const ${attr} = proxyer.${attr}\n`
23
- }
24
-
25
- const script = `
26
- import oox from 'oox'
27
- function RPC_${name} ( ) { }
28
- function dotCall ( name, action ) {
29
-
30
- return new Proxy ( RPC_${name}, {
31
-
32
- get ( target, key ) {
33
-
34
- return dotCall ( name, action ? action + '.' + key : key )
35
- },
36
-
37
- has ( target, key ) { return true },
38
-
39
- apply ( target, thisArg, args ) {
40
-
41
- return oox.rpc ( name, action, args )
42
- }
43
- } )
44
- }
45
- const proxyer = dotCall ( '${name}', '' )
46
- export default proxyer
47
- ${attrExports}
48
- `
49
-
50
- return script
51
- }
52
-
53
-
54
-
55
- function generateRPCProxyURL ( name, attributes ) {
56
-
57
- let searchText = ''
58
-
59
- if ( attributes.length ) {
60
-
61
- const search = new URLSearchParams ( )
62
-
63
- for ( const attr of attributes ) {
64
-
65
- search.append ( 'attr', attr )
66
- }
67
-
68
- searchText = '?' + search.toString ( )
69
- }
70
-
71
- return `oox://rpc/${name}.mjs${searchText}`
72
- }
73
-
74
-
75
-
76
- function isWebURL ( url ) {
77
-
78
- return url && ( url.startsWith ( 'https://' ) || url.startsWith ( 'http://' ) )
79
- }
80
-
81
-
82
-
83
- function isOOXURL ( url ) {
84
-
85
- return url && url.startsWith ( 'oox://' )
86
- }
87
-
88
-
89
-
90
- /**
91
- * make ESModule support dynamic <export *> attributes import
92
- * @param {string} importerSpecifier the import url
93
- * @param {string} specifier parentURL
94
- * @returns {string[]}
95
- */
96
- function getImportAttributes ( importerSpecifier, specifier ) {
97
-
98
- const attributes = [ ]
99
-
100
- const contents = fs.readFileSync ( importerSpecifier, 'utf-8' )
101
-
102
- // import * as xxx from "xxx"
103
- const mergeImport = contents.match ( new RegExp ( `import.+\\*\\s*as\\s+(\\w+)\\s+from\\s*["']${specifier}["']` ) )
104
-
105
- if ( mergeImport ) {
106
-
107
- const mergeName = mergeImport [ 1 ]
108
-
109
- const attributesIterator = contents.matchAll ( new RegExp ( `${mergeName}\\s*\\.\\s*(\\w+)`, 'g' ) )
110
-
111
- for ( const caseItem of attributesIterator ) {
112
-
113
- attributes.push ( caseItem [ 1 ] )
114
- }
115
- }
116
-
117
- // import { a, b, c } from 'xxx'
118
- const attributeImport = contents.match ( new RegExp ( `import.+{(.+)}\\s*from\\s*["']${specifier}["']` ) )
119
-
120
- if ( attributeImport ) {
121
-
122
- const definedAttributes = attributeImport [ 1 ].split ( ',' ).map ( v => v.trim ( ) ).filter ( v => !v.startsWith ( 'default' ) )
123
-
124
- attributes.push ( ...definedAttributes )
125
- }
126
-
127
- return Array.from ( new Set ( attributes ) )
128
- }
129
-
130
-
131
-
132
- /**
133
- * @param {string} specifier
134
- * @param {{
135
- * conditions: string[],
136
- * parentURL: string | undefined,
137
- * }} context
138
- * @param {Function} defaultResolve
139
- * @returns {Promise<{ url: string }>}
140
- */
141
- export async function resolve ( specifier, context, defaultResolve ) {
142
-
143
- const defaultSpecifer = specifier
144
-
145
- const { parentURL } = context
146
-
147
- // HTTP & HTTPS
148
- if ( isWebURL ( specifier ) ) {
149
-
150
- return { url: specifier }
151
- } else if ( isWebURL ( parentURL ) && ( specifier.startsWith ( '.' ) || specifier.startsWith ( '/' ) ) ) {
152
-
153
- return { url: new URL ( specifier, parentURL ).href }
154
- }
155
-
156
- // OOX special alias for web package
157
- if ( specifier === 'oox' ) {
158
-
159
- return {
160
- url: 'file://' + path.resolve ( './node_modules/oox/index.mjs' )
161
- }
162
- }
163
-
164
- const { entryFile } = oox.config
165
-
166
- // Relative specifier concat to absolute path
167
- if ( specifier.startsWith ( '.' ) && parentURL ) {
168
-
169
- specifier = path.posix.join ( path.dirname ( parentURL.replace ( 'file://', '' ) ), specifier )
170
- }
171
-
172
- // Windows pathname remove root sep
173
- if ( os.platform ( ) === 'win32' && specifier.startsWith ( '/' ) ) {
174
-
175
- specifier = specifier.replace ( '/', '' )
176
- }
177
-
178
- // OOX RPC Proxy URL generation
179
- if ( !specifier.endsWith ( entryFile.path ) && entryFile.group && specifier.startsWith ( entryFile.group ) ) {
180
-
181
- const subSpecifier = specifier.slice ( entryFile.group.length )
182
-
183
- const matchResult = subSpecifier.match ( /^\/?([\w-]+)(\/index)?(\.m?js)?$/ )
184
-
185
- if ( matchResult ) {
186
-
187
- const importerSpecifier = parentURL.replace ( os.platform ( ) === 'win32' ? /file:\/+/ : 'file://', '' )
188
-
189
- const attributes = getImportAttributes ( importerSpecifier, defaultSpecifer )
190
-
191
- return { url: generateRPCProxyURL ( matchResult [ 1 ], attributes ) }
192
- }
193
- }
194
-
195
- // restore Windows specifier protocol
196
- if ( os.platform ( ) === 'win32' && specifier.includes ( '/' ) && !specifier.startsWith ( 'file://' ) ) {
197
-
198
- specifier = 'file://' + specifier
199
- }
200
-
201
- return defaultResolve ( specifier, context, defaultResolve )
202
- }
203
-
204
-
205
-
206
- /**
207
- * @param {string} url
208
- * @param {{
209
- * format: string,
210
- * }} context If resolve settled with a `format`, that value is included here.
211
- * @param {Function} defaultLoad
212
- * @returns {Promise<{
213
- * format: string,
214
- * source: string | ArrayBuffer | SharedArrayBuffer | Uint8Array,
215
- * }>}
216
- */
217
- export async function load ( url, context, defaultLoad ) {
218
-
219
- if ( isWebURL ( url ) || isOOXURL ( url ) ) {
220
-
221
- return getSource ( url, context, defaultLoad )
222
- }
223
-
224
- return defaultLoad ( url, context, defaultLoad )
225
- }
226
-
227
-
228
-
229
- export function getFormat ( url, context, defaultGetFormat ) {
230
-
231
- if ( isWebURL ( url ) || isOOXURL ( url ) ) {
232
-
233
- return {
234
- format: 'module'
235
- }
236
- }
237
-
238
- return defaultGetFormat ( url, context, defaultGetFormat )
239
- }
240
-
241
-
242
-
243
- export function getSource ( url, context, defaultGetSource ) {
244
-
245
- if ( isWebURL ( url ) ) {
246
-
247
- const getMethod = url.startsWith ( 'https://' ) ? httpsGet : httpGet
248
-
249
- return new Promise ( ( resolve, reject ) => getMethod ( url, res => {
250
- let source = ''
251
- res
252
- .on ( 'data', chunk => source += chunk )
253
- .on ( 'end', () => resolve ( { format: 'module', source } ) )
254
- } ).on ( 'error', reject ) )
255
- } else if ( isOOXURL ( url ) ) {
256
-
257
- const mURL = new URL ( url )
258
-
259
- if ( mURL.host === 'rpc' ) {
260
-
261
- const regexp = /\/([\w-]+)\.mjs$/
262
-
263
- const matchResult = mURL.pathname.match ( regexp )
264
-
265
- // read all import attributes
266
- const attributes = mURL.searchParams.getAll ( 'attr' )
267
-
268
- const source = generateRPCProxyScript ( matchResult [ 1 ], attributes )
269
-
270
- return { format: 'module', source }
271
- }
272
- }
273
-
274
- return defaultGetSource ( url, context, defaultGetSource )
275
- }
276
-
277
-
278
-
279
- process.on ( 'unhandledRejection', console.error )
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
+ const ooxConfig = { }
13
+
14
+ function generateRPCProxyScript ( name, attributes = [ ] ) {
15
+
16
+ let attrExports = ''
17
+
18
+ for ( const attr of attributes ) {
19
+
20
+ attrExports += `\nconst RPC_ATTR_${attr} = proxyer.${attr}\n`
21
+ }
22
+
23
+ attrExports += '\nexport {\n'
24
+
25
+ for ( const attr of attributes ) {
26
+
27
+ attrExports += `\nRPC_ATTR_${attr} as ${attr},\n`
28
+ }
29
+
30
+ attrExports += '}\n'
31
+
32
+ const script = `
33
+ import oox from 'oox'
34
+ const attrMap = Object.create ( null )
35
+ function dotCall ( name, action ) {
36
+
37
+ return new Proxy ( function() {}, {
38
+
39
+ get ( target, key ) {
40
+ const attrKey = action ? action + '.' + key : key
41
+ if ( attrKey in attrMap ) {
42
+ return attrMap [ attrKey ]
43
+ }
44
+ const attrProxy = dotCall ( name, attrKey )
45
+ attrMap [ attrKey ] = attrProxy
46
+ return attrProxy
47
+ },
48
+
49
+ has ( target, key ) { return true },
50
+
51
+ apply ( target, thisArg, args ) {
52
+ return oox.rpc ( name, action, args )
53
+ }
54
+ } )
55
+ }
56
+ const proxyer = dotCall ( '${name}', '' )
57
+ export default proxyer
58
+ ${attrExports}
59
+ `
60
+
61
+ return script
62
+ }
63
+
64
+
65
+
66
+ function generateRPCProxyURL ( name, attributes ) {
67
+
68
+ let searchText = ''
69
+
70
+ if ( attributes.length ) {
71
+
72
+ const search = new URLSearchParams ( )
73
+
74
+ for ( const attr of attributes ) {
75
+
76
+ search.append ( 'attr', attr )
77
+ }
78
+
79
+ searchText = '?' + search.toString ( )
80
+ }
81
+
82
+ return `oox://rpc/${name}.mjs${searchText}`
83
+ }
84
+
85
+
86
+
87
+ function isWebURL ( url ) {
88
+
89
+ return url && ( url.startsWith ( 'https://' ) || url.startsWith ( 'http://' ) )
90
+ }
91
+
92
+
93
+
94
+ function isFileURL ( url ) {
95
+
96
+ return url && url.startsWith ( 'file://' )
97
+ }
98
+
99
+
100
+
101
+ function isOOXURL ( url ) {
102
+
103
+ return url && url.startsWith ( 'oox://' )
104
+ }
105
+
106
+
107
+
108
+ function pathCorrect ( path ) {
109
+
110
+ return os.platform ( ) === 'win32' && path.startsWith ( '/' ) ? path.replace ( '/', '' ) : path
111
+ }
112
+
113
+
114
+
115
+ /**
116
+ * make ESModule support dynamic <export *> attributes import
117
+ * @param {string} importerSpecifier the import url
118
+ * @param {string} specifier parentURL
119
+ * @returns {string[]}
120
+ */
121
+ function getImportAttributes ( importerSpecifier, specifier ) {
122
+
123
+ const attributes = [ ]
124
+
125
+ const contents = fs.readFileSync ( importerSpecifier, 'utf-8' )
126
+
127
+ // import * as xxx from "xxx"
128
+ const mergeImport = contents.match ( new RegExp ( `import.+\\*\\s*as\\s+(\\w+)\\s+from\\s*["']${specifier}["']` ) )
129
+
130
+ if ( mergeImport ) {
131
+
132
+ const mergeName = mergeImport [ 1 ]
133
+
134
+ const attributesIterator = contents.matchAll ( new RegExp ( `${mergeName}\\s*\\.\\s*(\\w+)`, 'g' ) )
135
+
136
+ for ( const caseItem of attributesIterator ) {
137
+
138
+ attributes.push ( caseItem [ 1 ] )
139
+ }
140
+ }
141
+
142
+ // import { a, b, c } from 'xxx'
143
+ const attributeImport = contents.match ( new RegExp ( `import.+{(.+)}\\s*from\\s*["']${specifier}["']` ) )
144
+
145
+ if ( attributeImport ) {
146
+
147
+ const definedAttributes = attributeImport [ 1 ].split ( ',' ).map ( v => v.trim ( ) ).filter ( v => !v.startsWith ( 'default' ) )
148
+
149
+ attributes.push ( ...definedAttributes )
150
+ }
151
+
152
+ return Array.from ( new Set ( attributes ) )
153
+ }
154
+
155
+
156
+
157
+ /**
158
+ * transform local call => RPC
159
+ * @param {string} originalSpecifier
160
+ * @param {string} specifier
161
+ * @param {string} parentURL
162
+ * @returns
163
+ */
164
+ function ooxRPCImportTransform ( originalSpecifier, specifier, parentURL ) {
165
+
166
+ const { entryInfo, ignore } = ooxConfig
167
+
168
+ const url = new URL ( specifier )
169
+
170
+ const groupURL = entryInfo.group ? new URL ( 'file://' + path.resolve ( '/', entryInfo.group ) ) : null
171
+
172
+ // OOX RPC Proxy URL generation
173
+ if ( !specifier.endsWith ( entryInfo.path ) && groupURL && url.href.startsWith ( groupURL.href ) ) {
174
+
175
+ const name = url.href.slice ( groupURL.href.length ).split ( '/' ).filter ( v => v ) [ 0 ].split ( '.' ) [ 0 ]
176
+
177
+ if ( ignore.includes ( name ) ) return null
178
+
179
+ const subSpecifier = url.href.slice ( groupURL.href.length )
180
+
181
+ const matchResult = subSpecifier.match ( /^\/?([\w-]+)(\/index)?(\.((\w?js)|(ts\w?)))?$/ )
182
+
183
+ if ( matchResult ) {
184
+
185
+ const importerSpecifier = parentURL.replace ( os.platform ( ) === 'win32' ? /file:\/+/ : 'file://', '' )
186
+
187
+ const attributes = getImportAttributes ( importerSpecifier, originalSpecifier )
188
+
189
+ return { shortCircuit: true, url: generateRPCProxyURL ( matchResult [ 1 ], attributes ) }
190
+ }
191
+ }
192
+
193
+ return null
194
+ }
195
+
196
+
197
+
198
+ /**
199
+ * Directory import supported
200
+ * Ignored '.ts|.js' suffix import supported
201
+ * @param {string} specifier
202
+ * @returns {string}
203
+ */
204
+ function directoryImportTransform ( specifier ) {
205
+
206
+ let filename = new URL ( specifier ).pathname
207
+
208
+ if ( os.platform ( ) === 'win32' && filename.startsWith ( '/' ) ) {
209
+
210
+ filename = filename.replace ( '/', '' )
211
+ }
212
+
213
+ const stat0 = fs.statSync ( filename, {
214
+ throwIfNoEntry: false
215
+ } )
216
+
217
+ if ( !stat0 || stat0.isDirectory ( ) ) {
218
+
219
+ const justNeedEntry = stat0 && stat0.isDirectory ( )
220
+
221
+ const dirname = justNeedEntry ? filename : path.dirname ( filename )
222
+
223
+ const stat1 = justNeedEntry ? stat0 : fs.statSync ( dirname, {
224
+ throwIfNoEntry: false
225
+ } )
226
+
227
+ // find entry file
228
+ if ( stat1 && stat1.isDirectory ( ) ) {
229
+
230
+ const paths = filename.split ( '/' )
231
+
232
+ const filenameWithoutExtension = justNeedEntry ? 'index' : paths.pop ( )
233
+
234
+ const matchEntryRegExp = new RegExp ( `^${filenameWithoutExtension}\\.((\\w?js)|(ts\\w?))$` )
235
+
236
+ const entries = fs.readdirSync ( dirname )
237
+
238
+ for ( const entry of entries ) {
239
+
240
+ if ( !matchEntryRegExp.test ( entry ) ) continue
241
+
242
+ paths.push ( entry )
243
+
244
+ specifier = 'file://' + paths.join ( '/' )
245
+
246
+ break
247
+ }
248
+ }
249
+ }
250
+
251
+ return specifier
252
+ }
253
+
254
+
255
+
256
+ /**
257
+ * NodeJS version 18 or higher
258
+ * @param {*} param0
259
+ */
260
+ export async function initialize ( { ooxConfig: config } ) {
261
+
262
+ if ( !config ) {
263
+
264
+ throw new Error ( 'Loader initialize failed' )
265
+ }
266
+
267
+ Object.assign ( ooxConfig, config )
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
+ const url = ( await defaultResolve ( specifier, {
300
+ conditions: [ 'node', 'import', 'module-sync', 'node-addons' ],
301
+ parentURL: import.meta.url
302
+ }, defaultResolve ) ).url
303
+
304
+ return {
305
+ shortCircuit: true,
306
+ url
307
+ // url: await import.meta.resolve ( specifier )
308
+ }
309
+ }
310
+
311
+ if ( !isFileURL ( specifier ) ) {
312
+
313
+ try {
314
+
315
+ specifier = ( await defaultResolve ( specifier, context, defaultResolve ) ).url
316
+ } catch ( error ) {
317
+
318
+ if ( !isFileURL ( parentURL ) ) throw error
319
+
320
+ const _specifier = directoryImportTransform ( new URL ( specifier, parentURL ).href )
321
+
322
+ if ( _specifier === specifier ) throw error
323
+
324
+ specifier = _specifier
325
+ }
326
+ }
327
+
328
+ const ooxRPCTransform = ooxRPCImportTransform ( originalSpecifier, specifier, parentURL )
329
+
330
+ if ( ooxRPCTransform ) return ooxRPCTransform
331
+
332
+ return defaultResolve ( specifier, context, defaultResolve )
333
+ }
334
+
335
+
336
+
337
+ /**
338
+ * @param {string} url
339
+ * @param {{
340
+ * format: string,
341
+ * }} context If resolve settled with a `format`, that value is included here.
342
+ * @param {Function} defaultLoad
343
+ * @returns {Promise<{
344
+ * format: string,
345
+ * source: string | ArrayBuffer | SharedArrayBuffer | Uint8Array,
346
+ * }>}
347
+ */
348
+ export async function load ( url, context, defaultLoad ) {
349
+
350
+ if ( isWebURL ( url ) || isOOXURL ( url ) ) {
351
+
352
+ return getSource ( url, context, defaultLoad )
353
+ }
354
+
355
+ return defaultLoad ( url, context, defaultLoad )
356
+ }
357
+
358
+
359
+
360
+ export function getSource ( url, context, defaultGetSource ) {
361
+
362
+ if ( isWebURL ( url ) ) {
363
+
364
+ const getMethod = url.startsWith ( 'https://' ) ? httpsGet : httpGet
365
+
366
+ return new Promise ( ( resolve, reject ) => getMethod ( url, res => {
367
+ let source = ''
368
+ res
369
+ .on ( 'data', chunk => source += chunk )
370
+ .on ( 'end', () => resolve ( { shortCircuit: true, format: 'module', source } ) )
371
+ } ).on ( 'error', reject ) )
372
+ } else if ( isOOXURL ( url ) ) {
373
+
374
+ const mURL = new URL ( url )
375
+
376
+ if ( mURL.host === 'rpc' ) {
377
+
378
+ const regexp = /\/([\w-]+)\.mjs$/
379
+
380
+ const matchResult = mURL.pathname.match ( regexp )
381
+
382
+ // read all import attributes
383
+ const attributes = mURL.searchParams.getAll ( 'attr' )
384
+
385
+ const source = generateRPCProxyScript ( matchResult [ 1 ], attributes )
386
+
387
+ return { shortCircuit: true, format: 'module', source }
388
+ }
389
+ }
390
+
391
+ return defaultGetSource ( url, context, defaultGetSource )
392
+ }