@nejs/basic-extensions 2.21.0 → 2.21.5
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/.idea/markdown.xml +8 -0
- package/.idea/modules.xml +8 -0
- package/.idea/ne-basic-extensions.iml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CODE_STYLE.md +393 -0
- package/CODING_PHILOSOPHY.md +36 -0
- package/DOCUMENTATION_GUIDELINES.md +221 -0
- package/dist/@nejs/{basic-extensions.bundle.2.21.0.js → basic-extensions.bundle.2.21.5.js} +6 -6
- package/dist/@nejs/basic-extensions.bundle.2.21.5.js.map +7 -0
- package/dist/cjs/big.int.extension.js +153 -45
- package/dist/cjs/big.int.extension.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/math.extension.d.ts +14 -0
- package/dist/cjs/math.extension.js +71 -0
- package/dist/cjs/math.extension.js.map +1 -0
- package/dist/cjs/number.extension.js +17 -0
- package/dist/cjs/number.extension.js.map +1 -1
- package/dist/mjs/big.int.extension.js +153 -45
- package/dist/mjs/big.int.extension.js.map +1 -1
- package/dist/mjs/index.d.ts +1 -1
- package/dist/mjs/index.js +2 -0
- package/dist/mjs/index.js.map +1 -1
- package/dist/mjs/math.extension.d.ts +14 -0
- package/dist/mjs/math.extension.js +68 -0
- package/dist/mjs/math.extension.js.map +1 -0
- package/dist/mjs/number.extension.js +17 -0
- package/dist/mjs/number.extension.js.map +1 -1
- package/package.json +2 -2
- package/repl.history +30 -30
- package/src/big.int.extension.js +171 -45
- package/src/index.js +2 -0
- package/src/math.extension.js +73 -0
- package/src/number.extension.js +18 -0
- package/dist/@nejs/basic-extensions.bundle.2.21.0.js.map +0 -7
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="MarkdownSettings">
|
|
4
|
+
<option name="previewPanelProviderInfo">
|
|
5
|
+
<ProviderInfo name="Mermaid Studio (Chromium browser)" className="MermaidStudio_z.MermaidStudio_R.MermaidStudio_n.MermaidStudio_Q.MermaidStudio_U.MermaidStudio__.JCEFHtmlPanelProvider" />
|
|
6
|
+
</option>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/ne-basic-extensions.iml" filepath="$PROJECT_DIR$/.idea/ne-basic-extensions.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
package/.idea/vcs.xml
ADDED
package/CODE_STYLE.md
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# Code Style Guide
|
|
2
|
+
|
|
3
|
+
This document is written for AI assistants generating code in `@nejs` repositories. These are not aspirational conventions — they are patterns extracted from existing code. Follow them precisely so that generated code is indistinguishable from hand-written code.
|
|
4
|
+
|
|
5
|
+
## Semicolons
|
|
6
|
+
|
|
7
|
+
Do not use semicolons. Rely on ASI (Automatic Semicolon Insertion). The one exception is class property definitions declared outside of any method or constructor — these get semicolons.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// no semicolons in statements, imports, returns, etc.
|
|
11
|
+
const name = 'Brie'
|
|
12
|
+
const items = [1, 2, 3]
|
|
13
|
+
return this.#settled
|
|
14
|
+
import { Patch } from '@nejs/extension'
|
|
15
|
+
|
|
16
|
+
// semicolons on class property definitions
|
|
17
|
+
export class Deferred extends Promise {
|
|
18
|
+
#promise = null;
|
|
19
|
+
#reject = null;
|
|
20
|
+
#resolve = null;
|
|
21
|
+
|
|
22
|
+
value = null;
|
|
23
|
+
reason = null;
|
|
24
|
+
|
|
25
|
+
#settled = false;
|
|
26
|
+
|
|
27
|
+
constructor(options) {
|
|
28
|
+
// no semicolons inside methods
|
|
29
|
+
const config = parseOptions(options)
|
|
30
|
+
let _resolve, _reject
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quotes
|
|
36
|
+
|
|
37
|
+
Use single quotes. Reserve double quotes for strings that contain single quotes. Use template literals when interpolating.
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { Patch } from '@nejs/extension'
|
|
41
|
+
const tag = 'EnumValue'
|
|
42
|
+
const msg = `${this.major}.${this.minor}.${this.patch}`
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Line Length
|
|
46
|
+
|
|
47
|
+
Aim for 80 columns or fewer. Wrap lines when it makes reading the code more pleasurable — in both code and JSDoc prose. Do not forcibly wrap to the detriment of legibility; a line that reads clearly at 85 columns is better than one awkwardly broken at 79. The goal is readability, not a hard ruler.
|
|
48
|
+
|
|
49
|
+
## Indentation
|
|
50
|
+
|
|
51
|
+
2 spaces. No tabs.
|
|
52
|
+
|
|
53
|
+
## Variable Declarations
|
|
54
|
+
|
|
55
|
+
`const` by default. `let` only when reassignment is required. Never `var`.
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const config = parseOptions(opts)
|
|
59
|
+
let _resolve, _reject
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Arrow Function Parameters
|
|
63
|
+
|
|
64
|
+
Always parenthesize, even with a single parameter.
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
values.forEach((value) => this.add(value))
|
|
68
|
+
this.some((element) => element === value)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Arrow Functions vs `function`
|
|
72
|
+
|
|
73
|
+
Use arrow functions for callbacks, short lambdas, and closures that don't need their own `this`. Use `function` declarations or named function expressions when the function needs `this` binding, is a Symbol-keyed method, or benefits from a name for stack traces.
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
// arrow for callbacks
|
|
77
|
+
entries.filter((e) => Array.isArray(e) && e.length === 2)
|
|
78
|
+
|
|
79
|
+
// named function for Symbol-keyed methods that use `this`
|
|
80
|
+
[Symbol.for('compare')]: data(
|
|
81
|
+
function compareValue(to) {
|
|
82
|
+
const {real: lReal, value: lValue} = this
|
|
83
|
+
// ...
|
|
84
|
+
}, false, true, false
|
|
85
|
+
)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Braces on Single-Statement Bodies
|
|
89
|
+
|
|
90
|
+
If the body of an `if`, `for`, or similar block is a single short statement, omit the braces. Brace everything else.
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// unbraced — single short statement
|
|
94
|
+
if (!isLEnum || !isREnum)
|
|
95
|
+
return false
|
|
96
|
+
|
|
97
|
+
if (!Array.isArray(array))
|
|
98
|
+
return false
|
|
99
|
+
|
|
100
|
+
// braced — multi-line or complex body
|
|
101
|
+
if (config?.resolve && config?.reject) {
|
|
102
|
+
throw new TypeError(
|
|
103
|
+
'resolve and reject options cannot be simultaneously provided'
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## `else` and `else if` Placement
|
|
109
|
+
|
|
110
|
+
Drop `else` and `else if` to a new line. Do not cuddle them on the closing brace line.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
if (config?.resolve) {
|
|
114
|
+
this.#resolve(config?.resolve)
|
|
115
|
+
}
|
|
116
|
+
else if (config?.reject) {
|
|
117
|
+
this.#reject(config?.reject)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
if (Descriptor.isDescriptor(object)) {
|
|
123
|
+
this._desc = object
|
|
124
|
+
}
|
|
125
|
+
else if (isObject(object) && isValidKey(key)) {
|
|
126
|
+
this._desc = Object.getOwnPropertyDescriptor(object, key)
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Ternary Formatting
|
|
131
|
+
|
|
132
|
+
Short ternaries stay on one line. Long ternaries break across lines with `?` and `:` indented, and the closing paren on its own line.
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// short
|
|
136
|
+
const store = is.object(store) ? store : undefined
|
|
137
|
+
|
|
138
|
+
// long
|
|
139
|
+
const config = (options && typeof(options) === 'object'
|
|
140
|
+
? options
|
|
141
|
+
: {}
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Multi-Line Boolean Expressions
|
|
146
|
+
|
|
147
|
+
Trailing `&&` or `||` at the end of each line. Subsequent conditions align under the first.
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
return this.major === otherVersion.major &&
|
|
151
|
+
this.minor === otherVersion.minor &&
|
|
152
|
+
this.patch === otherVersion.patch &&
|
|
153
|
+
this.prerelease === otherVersion.prerelease
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Parenthesized Multi-Line Returns
|
|
157
|
+
|
|
158
|
+
Wrap complex return expressions in parentheses. Opening paren on the `return` line.
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
return (
|
|
162
|
+
value instanceof Function &&
|
|
163
|
+
String(value).includes('=>') &&
|
|
164
|
+
!String(value).startsWith('bound') &&
|
|
165
|
+
!Reflect.has(value, 'prototype')
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
return (array
|
|
171
|
+
.map((value) => (typeof value))
|
|
172
|
+
.some((value) => value === 'symbol')
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Destructuring
|
|
177
|
+
|
|
178
|
+
Destructure at point of use. Renaming during destructuring is common and encouraged when it clarifies intent or prevents collisions.
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const { MAJOR, MINOR, PATCH } = SemVer
|
|
182
|
+
const {real: lReal, value: lValue, name: lName, type: lType} = this
|
|
183
|
+
const {real: rReal, value: rValue, name: rName, type: rType} = toObj
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Static Getter Constants
|
|
187
|
+
|
|
188
|
+
One-liner format when the body is a single return.
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
static get FULL() { return '*' }
|
|
192
|
+
static get MAJOR() { return 'major' }
|
|
193
|
+
static get MINOR() { return 'minor' }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Short vs Expanded Getters
|
|
197
|
+
|
|
198
|
+
Short getters with trivial bodies can be one-liners. Anything beyond a simple return gets expanded.
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// one-liner
|
|
202
|
+
get hasObject() { return isObject(this._object) }
|
|
203
|
+
|
|
204
|
+
// expanded
|
|
205
|
+
get settled() {
|
|
206
|
+
return this.#settled
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get start() {
|
|
210
|
+
return typeof this.#start === 'function' ? this.#start() : this.#start
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Switch Statements
|
|
215
|
+
|
|
216
|
+
Compact case/return on the same line when cases are uniform and short. Use `break` or `return` — never fall through silently.
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
switch (part) {
|
|
220
|
+
case MAJOR: this[part] = parseInt(semverString); return
|
|
221
|
+
case MINOR: this[part] = parseInt(semverString); return
|
|
222
|
+
case PATCH: this[part] = parseInt(semverString); return
|
|
223
|
+
case PRERELEASE: this[part] = semverString; return
|
|
224
|
+
case METADATA: this[part] = semverString; return
|
|
225
|
+
default:
|
|
226
|
+
break
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
For switches with longer case bodies, expand normally:
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
switch (hint) {
|
|
234
|
+
case 'number':
|
|
235
|
+
return parseFloat(`${this.major}.${this.minor}`)
|
|
236
|
+
case 'string':
|
|
237
|
+
return this.get()
|
|
238
|
+
default:
|
|
239
|
+
return null
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## `typeof` With Parentheses
|
|
244
|
+
|
|
245
|
+
`typeof` is sometimes written with parentheses as though it were a function call. Both forms appear, but the parenthesized form is the more natural one in this codebase.
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
typeof(options) === 'object'
|
|
249
|
+
typeof(config?.executor) === 'function'
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Boolean Coercion and Bitwise Idioms
|
|
253
|
+
|
|
254
|
+
`!!` for explicit boolean coercion. `!!~` with `indexOf` for existence checks. These are deliberate, not accidental.
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
return !!this.find((entry) => entry === value)
|
|
258
|
+
return this.some((element) => !!~values.indexOf(element))
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Comparison Operators
|
|
262
|
+
|
|
263
|
+
Prefer strict equality (`===`). Loose equality (`==`) is acceptable when explicitly intended — it appears in methods like `oneIs` where the caller opts in via a parameter.
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
// default: strict
|
|
267
|
+
const _skip = this.value === Symbol.for('Enum.NonAssociatedValue')
|
|
268
|
+
|
|
269
|
+
// intentional loose equality controlled by parameter
|
|
270
|
+
doubleEqualsOkay ? element == value : element === value
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Optional Chaining and Nullish Coalescing
|
|
274
|
+
|
|
275
|
+
Use freely. These are idiomatic throughout the codebase.
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
this.inclusive = start?.inclusive ?? true
|
|
279
|
+
configurable: baseDescriptor?.configurable ?? true
|
|
280
|
+
const lineIndent = /^(\s+)/.exec(line)?.[1]?.length ?? 0
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Imports
|
|
284
|
+
|
|
285
|
+
External dependencies first, then internal modules, separated by a blank line. Named imports with destructuring.
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
import { Extension, Patch } from '@nejs/extension'
|
|
289
|
+
|
|
290
|
+
import { Deferred } from './async/deferred.js'
|
|
291
|
+
import { Callable } from './core/callable.js'
|
|
292
|
+
import { Range } from './core/range.js'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Blank Lines
|
|
296
|
+
|
|
297
|
+
Use blank lines to separate logical sections within a function. One blank line between conceptual steps. Do not over-space — no double blank lines, no blank lines after opening braces or before closing braces.
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
constructor(options) {
|
|
301
|
+
const config = (options && typeof(options) === 'object'
|
|
302
|
+
? options
|
|
303
|
+
: {}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
if (config?.resolve && config?.reject) {
|
|
307
|
+
throw new TypeError(
|
|
308
|
+
'resolve and reject options cannot be simultaneously provided'
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let _resolve, _reject
|
|
313
|
+
|
|
314
|
+
super((resolve, reject) => {
|
|
315
|
+
_resolve = resolve
|
|
316
|
+
_reject = reject
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Class Structure
|
|
321
|
+
|
|
322
|
+
Order within a class body:
|
|
323
|
+
|
|
324
|
+
1. `#` private fields (with defaults)
|
|
325
|
+
2. Public instance fields (with defaults)
|
|
326
|
+
3. `constructor`
|
|
327
|
+
4. Instance getters/setters
|
|
328
|
+
5. Instance methods
|
|
329
|
+
6. `Symbol`-keyed methods and getters (`Symbol.toPrimitive`, `Symbol.iterator`, `Symbol.toStringTag`, `Symbol.species`)
|
|
330
|
+
7. Static getters (constants)
|
|
331
|
+
8. Static methods
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
export class Deferred extends Promise {
|
|
335
|
+
#promise = null
|
|
336
|
+
#reject = null
|
|
337
|
+
#resolve = null
|
|
338
|
+
|
|
339
|
+
value = null
|
|
340
|
+
reason = null
|
|
341
|
+
|
|
342
|
+
#settled = false
|
|
343
|
+
|
|
344
|
+
constructor(options) { ... }
|
|
345
|
+
|
|
346
|
+
get settled() { ... }
|
|
347
|
+
get promise() { ... }
|
|
348
|
+
|
|
349
|
+
resolve(value) { ... }
|
|
350
|
+
reject(reason) { ... }
|
|
351
|
+
|
|
352
|
+
static get [Symbol.species]() { ... }
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Symbols
|
|
357
|
+
|
|
358
|
+
Use `Symbol.for()` for shared/interoperable keys. Define symbol accessors as static getters with the `k` prefix convention.
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
static get kHandler() {
|
|
362
|
+
return Symbol.for('callable.handler')
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
static get kFunction() {
|
|
366
|
+
return Symbol.for('callable.function')
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## String Concatenation
|
|
371
|
+
|
|
372
|
+
Template literals for interpolation. The `+` operator is acceptable for simple one-off concatenation (e.g., `key + '.associated'`).
|
|
373
|
+
|
|
374
|
+
## Trailing Commas
|
|
375
|
+
|
|
376
|
+
Not enforced in either direction. Do not add them retroactively to existing code, do not remove them from existing code. When writing new multi-line object/array literals, trailing commas are acceptable.
|
|
377
|
+
|
|
378
|
+
## Error Handling
|
|
379
|
+
|
|
380
|
+
Throw specific error types (`TypeError`, `Error`) with descriptive string messages. For invalid input in utility/library code, prefer branching and returning a safe default over throwing.
|
|
381
|
+
|
|
382
|
+
```javascript
|
|
383
|
+
// throw for contract violations
|
|
384
|
+
throw new TypeError(
|
|
385
|
+
'resolve and reject options cannot be simultaneously provided'
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
// branch for soft failures
|
|
389
|
+
if (!this.isObject(obj)) {
|
|
390
|
+
console.warn('Object.add() must receive an object for `toObject`')
|
|
391
|
+
return obj
|
|
392
|
+
}
|
|
393
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Coding Philosophy & Architectural Preferences
|
|
2
|
+
|
|
3
|
+
Observations drawn from the `@nejs/foundation` and `@nejs/basic-extensions` codebases.
|
|
4
|
+
|
|
5
|
+
## Core Beliefs
|
|
6
|
+
|
|
7
|
+
**JavaScript maximalism.** The language's built-in types are *incomplete*, not broken. Rather than avoiding prototype modification, the approach is to build a reversible patch infrastructure (`@nejs/extension`) to do it correctly. The `Controls` system with enable/disable is a disciplined concession to orthodoxy — `arr.first` should just *work*.
|
|
8
|
+
|
|
9
|
+
**Protocol-oriented thinking.** Heavy use of `Symbol.iterator`, `Symbol.species`, `Symbol.toPrimitive`, `Symbol.toStringTag`, and custom symbols throughout. Objects are participants in protocols. The `Callable` class is the clearest expression: a single entity that *is* both function and object, participating in both protocols simultaneously through comprehensive Proxy traps.
|
|
10
|
+
|
|
11
|
+
**Expressiveness as a core value.** The `if*` conditional pattern on every type checker, the `is`/`has`/`as`/`si` global toolkit, the Deferred exposing resolve/reject — these convert imperative control flow into expressions. `si.array(val, transform, fallback)` over an if/else block. Functional programming flavor without dogma.
|
|
12
|
+
|
|
13
|
+
**Descriptor-level awareness.** Properties are not key/value pairs — they are full descriptors with enumeration, configurability, and accessor semantics. Visibility handlers (`kMutablyHidden`, `kImmutablyVisible`), `Object.add()` with storage-backed accessors, and the `Descriptor` class form an entire vocabulary around `Object.defineProperty`.
|
|
14
|
+
|
|
15
|
+
## Architecture Preferences
|
|
16
|
+
|
|
17
|
+
**Bottom-up, layered abstraction.** `@nejs/extension` (patch mechanism) → `@nejs/basic-extensions` (enriched primitives) → `@nejs/foundation` (higher-level patterns). Each layer assumes the one below. The goal is not an application framework — it is building the *language you wish you had*, then writing applications in that language.
|
|
18
|
+
|
|
19
|
+
**Enrich the base rather than wrap it.** Prefer extending built-in types with missing capabilities over creating parallel utility namespaces. Rich methods on every type (`set.map()`, `set.reduce()`, `arr.first`, `arr.last`) in the Ruby/Smalltalk tradition of making built-ins joyful to use.
|
|
20
|
+
|
|
21
|
+
**Convention applied uniformly.** Every `isX` gets a corresponding `ifX`. Every type gets the same treatment. Patterns are systematic, not ad-hoc.
|
|
22
|
+
|
|
23
|
+
## Implementation Conventions
|
|
24
|
+
|
|
25
|
+
- `#` private fields over closures for encapsulation
|
|
26
|
+
- Custom symbols (prefixed `k`) for internal access keys (`kHandler`, `kFunction`, `kDescriptorStore`)
|
|
27
|
+
- Comprehensive JSDoc over TypeScript — types as documentation, not as a compilation step (tsc used as transpiler, not type checker)
|
|
28
|
+
- Well-known Symbols implemented wherever semantically appropriate (`Symbol.iterator`, `Symbol.species`, `Symbol.toPrimitive`, `Symbol.toStringTag`)
|
|
29
|
+
- Full Proxy trap coverage when proxying (not partial implementations)
|
|
30
|
+
- Method chaining where it fits naturally (e.g., SemVer increment/decrement)
|
|
31
|
+
|
|
32
|
+
## Influences
|
|
33
|
+
|
|
34
|
+
- **Ruby/Smalltalk** — open classes, rich standard library methods on built-in types, ergonomic APIs
|
|
35
|
+
- **jQuery** — deferred pattern, the philosophy that APIs should be ergonomic first
|
|
36
|
+
- **JavaScript metaprogramming** — Proxy, Reflect, Symbol, and property descriptors are foundational tools, not curiosities
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Documentation & Style Guidelines
|
|
2
|
+
|
|
3
|
+
How documentation is written across `@nejs` projects. These aren't aspirational rules — they're patterns extracted from existing code. Follow them so that a reader who's seen three docs can predict the structure of the fourth.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
Code alone never captures intent and circumstance. Documentation exists to explain *why* something exists in the world, what design pressures shaped it, and what the reader should expect when they use it. The JSDoc is the primary type system — not TypeScript, not Flow — and it serves double duty: human-readable narrative *and* IDE-consumable type information (WebStorm, VS Code, etc.).
|
|
8
|
+
|
|
9
|
+
TypeScript is avoided unless externally required. JSDoc `@param`, `@returns`, `@typedef`, and `@type` annotations carry the type information. This keeps the source as plain JavaScript while giving IDEs everything they need for autocomplete, hover docs, and type checking.
|
|
10
|
+
|
|
11
|
+
## JSDoc Structure
|
|
12
|
+
|
|
13
|
+
A complete doc block follows this order. Not every section appears on every method — scale detail to complexity.
|
|
14
|
+
|
|
15
|
+
1. **Opening narrative** — What it does and *why it exists*. For classes, include historical context or lineage where relevant. For methods, lead with the practical purpose.
|
|
16
|
+
2. **`@typedef` / `@callback`** — For complex option objects or function signatures, define them inline or reference a shared typedef. Use TypeScript-style interface notation inside fenced code blocks when the shape is complex.
|
|
17
|
+
3. **`@param`** — Every parameter documented. Include union types with `|`, defaults with `[param=default]`, conditional requirements ("Required if `start` is a number"), and constraints ("Must be non-zero").
|
|
18
|
+
4. **`@returns`** — What comes back and under what conditions.
|
|
19
|
+
5. **`@throws`** — When and why.
|
|
20
|
+
6. **`@example`** — Concrete, runnable scenarios (see Examples section below).
|
|
21
|
+
|
|
22
|
+
### Simple methods get simple docs
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
/**
|
|
26
|
+
* The `isString` method does exactly what one would expect. It returns
|
|
27
|
+
* true if the string matches typeof or instanceof as a string.
|
|
28
|
+
*
|
|
29
|
+
* @param {*} value checks to see if the `value` is a string
|
|
30
|
+
* @returns {boolean} `true` if it is a `String`, `false` otherwise
|
|
31
|
+
*/
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Three lines of description, one param, one return. Done.
|
|
35
|
+
|
|
36
|
+
### Complex methods get structured docs
|
|
37
|
+
|
|
38
|
+
For methods with multiple modes, option objects, or non-obvious behavior, use visual structure — tables, fenced code blocks for option lists, multiple examples showing different usage patterns:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
/**
|
|
42
|
+
* Applies Select Graphic Rendition (SGR) parameters to a given message
|
|
43
|
+
* for styling in terminal environments. This function allows for the
|
|
44
|
+
* dynamic styling of text output using ANSI escape codes.
|
|
45
|
+
*
|
|
46
|
+
* Colors:
|
|
47
|
+
* ```
|
|
48
|
+
* 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
|
49
|
+
* ```
|
|
50
|
+
* Color Specifiers:
|
|
51
|
+
* ```
|
|
52
|
+
* 'fg' -> foreground | 'bg' -> background | 'bright' -> bright colors
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* Modes:
|
|
56
|
+
* ```
|
|
57
|
+
* 'blink' or 'k' | 'conceal' or 'c' | 'italics' or 'i' | 'strike' or 's'
|
|
58
|
+
* 'bold' or 'b' | 'dim' or 'd' | 'negative' or 'n' | 'underline' or 'u'
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @param {string} message The message to be styled.
|
|
62
|
+
* @param {...string} useModes Styling modes — colors, specifiers, and
|
|
63
|
+
* decorations. Can be combined in a single comma-separated string or
|
|
64
|
+
* passed as separate arguments.
|
|
65
|
+
* @returns {string} The styled string wrapped in ANSI escape codes.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* sgr('Hello', 'red')
|
|
69
|
+
* sgr('World', 'green,bold')
|
|
70
|
+
* sgr('Example', 'bluebgbright')
|
|
71
|
+
*
|
|
72
|
+
* // Shorthand syntax
|
|
73
|
+
* sgr('hello', 'biu') // bold, italics, underline
|
|
74
|
+
* sgr('hello', 'bi,redfg') // bold, italics, red foreground
|
|
75
|
+
*/
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Examples
|
|
79
|
+
|
|
80
|
+
Examples are stories, not syntax diagrams. Show a scenario the reader can inhabit.
|
|
81
|
+
|
|
82
|
+
### Do this
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
/**
|
|
86
|
+
* @example
|
|
87
|
+
* // assume this pure function exists someplace
|
|
88
|
+
* const lastThenFirstThenMiddle = function() {
|
|
89
|
+
* let middle = ''
|
|
90
|
+
*
|
|
91
|
+
* if (this.middleName)
|
|
92
|
+
* middle = ` ${this.middleName}`
|
|
93
|
+
*
|
|
94
|
+
* return `${this.lastName}, ${this.firstName}${middle}`
|
|
95
|
+
* }
|
|
96
|
+
*
|
|
97
|
+
* let people = []
|
|
98
|
+
* for await (const model of await getSomePeople()) {
|
|
99
|
+
* let parseName = new Callable(model, lastThenFirstThenMiddle)
|
|
100
|
+
* people.push(parseName())
|
|
101
|
+
* }
|
|
102
|
+
*
|
|
103
|
+
* console.log(people)
|
|
104
|
+
* // Might look like ['Doe, Jane', 'Smith, Sally Joanne']
|
|
105
|
+
*/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This shows: the setup, the loop, the integration with async code, and what the output "might look like." The reader understands the *use case*, not just the constructor signature.
|
|
109
|
+
|
|
110
|
+
### Teach through contrast
|
|
111
|
+
|
|
112
|
+
When a method's behavior has meaningful boundaries, show both sides:
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
/**
|
|
116
|
+
* @example
|
|
117
|
+
* // Note the differences between these
|
|
118
|
+
* const object = { get name() { return "Brie"; } };
|
|
119
|
+
* const descriptor = Object.getOwnPropertyDescriptor(object, 'name');
|
|
120
|
+
* is.callable(object); // false
|
|
121
|
+
* is.callable(descriptor); // true
|
|
122
|
+
*/
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Types in JSDoc
|
|
126
|
+
|
|
127
|
+
JSDoc is the type system. Write types for the IDE, narrative for the human.
|
|
128
|
+
|
|
129
|
+
### Primitives and unions
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
@param {string|number} value
|
|
133
|
+
@param {boolean} [inclusive=true]
|
|
134
|
+
@param {*} value
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Complex option objects
|
|
138
|
+
|
|
139
|
+
Use TypeScript-style interface notation inside the doc block when the shape warrants it:
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
/**
|
|
143
|
+
* The constructor takes an object called `options`. It can have the
|
|
144
|
+
* following properties:
|
|
145
|
+
*
|
|
146
|
+
* ```
|
|
147
|
+
* interface BaseDeferredOptions {
|
|
148
|
+
* // Deferreds store the value or reason. To turn this off, pass true
|
|
149
|
+
* doNotTrackAnswers?: boolean;
|
|
150
|
+
* }
|
|
151
|
+
*
|
|
152
|
+
* interface DeferredResolveOptions extends BaseDeferredOptions {
|
|
153
|
+
* resolve: any; // Auto-resolve with this value
|
|
154
|
+
* reject?: never;
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @param {BaseDeferredOptions|DeferredResolveOptions} options
|
|
159
|
+
*/
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This gives the reader a typed shape to reference while keeping the source as plain JavaScript.
|
|
163
|
+
|
|
164
|
+
### Callback signatures
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
@param {function(number, number): boolean} compareFn
|
|
168
|
+
@callback StepFunction
|
|
169
|
+
@returns {number}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Inline Comments
|
|
173
|
+
|
|
174
|
+
### Explain decisions, not mechanics
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
// If the target function is a big arrow function, convert it to
|
|
178
|
+
// a bindable function. Note that big arrow functions will receive
|
|
179
|
+
// the handler as its first parameter; so account for that.
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
This explains the *why* (arrow functions can't be bound) and the *consequence* (handler becomes first param). The code shows the *how*.
|
|
183
|
+
|
|
184
|
+
### Be honest about shortcuts
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
// being lazy here, someone has defined we make an accessor but
|
|
188
|
+
// wants the default accessor behaviors with an associated store
|
|
189
|
+
// made by us.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Leave breadcrumbs for future you
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
// NOTE to self; this is repeated here otherwise a circular reference from
|
|
196
|
+
// Object<->Function<->Global occurs. See original source in global.this.js
|
|
197
|
+
// {@see globalThis.isThenElse}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Voice
|
|
201
|
+
|
|
202
|
+
The documentation voice is **knowledgeable but approachable**. It's a teacher explaining to a smart student, not a reference manual reciting signatures.
|
|
203
|
+
|
|
204
|
+
**Signature phrases** (use naturally, not mechanically):
|
|
205
|
+
- "This is useful for scenarios where..."
|
|
206
|
+
- "This allows..."
|
|
207
|
+
- "Returns `true` if... `false` otherwise"
|
|
208
|
+
- "If... is provided..."
|
|
209
|
+
- "Optionally..."
|
|
210
|
+
|
|
211
|
+
**Formatting conventions:**
|
|
212
|
+
- Backticks around all code references in prose: `undefined`, `handler`, `Array.prototype`
|
|
213
|
+
- `{@link ClassName}` for cross-references to other documented types
|
|
214
|
+
- Periods at end of `@param` and `@returns` descriptions
|
|
215
|
+
- Fenced code blocks inside JSDoc for visual structure (option tables, mode lists)
|
|
216
|
+
|
|
217
|
+
## What Not to Document
|
|
218
|
+
|
|
219
|
+
- Don't over-document the obvious. If the method is `isString` and it checks if something is a string, three lines suffice.
|
|
220
|
+
- Don't repeat the function signature in prose. The params are already listed — the narrative should explain *intent*, not restate types.
|
|
221
|
+
- Don't write generic advice ("always validate inputs"). Document what *this specific code* does and why.
|