paperlint 7.1.0 → 7.2.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/.github/workflows/ci.yml +3 -0
- package/.github/workflows/deadlines.yml +100 -0
- package/README.md +6 -0
- package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
- package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.js +26 -0
- package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
- package/dist/adapters/hotcrp/deadlines.js +134 -0
- package/dist/adapters/hotcrp/deadlines.js.map +1 -0
- package/dist/adapters/hotcrp/index.d.ts +2 -0
- package/dist/adapters/hotcrp/index.d.ts.map +1 -1
- package/dist/adapters/hotcrp/index.js +2 -0
- package/dist/adapters/hotcrp/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +40 -5
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +24 -0
- package/dist/cycle-rules.d.ts.map +1 -0
- package/dist/cycle-rules.js +212 -0
- package/dist/cycle-rules.js.map +1 -0
- package/dist/domain/cycle.d.ts +148 -0
- package/dist/domain/cycle.d.ts.map +1 -0
- package/dist/domain/cycle.js +281 -0
- package/dist/domain/cycle.js.map +1 -0
- package/dist/domain/deadline.d.ts +128 -0
- package/dist/domain/deadline.d.ts.map +1 -0
- package/dist/domain/deadline.js +281 -0
- package/dist/domain/deadline.js.map +1 -0
- package/dist/eslint-rules/cycle-stage.d.ts +39 -0
- package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
- package/dist/eslint-rules/cycle-stage.js +119 -0
- package/dist/eslint-rules/cycle-stage.js.map +1 -0
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -0
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/paper-settings.d.ts +7 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +61 -8
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/venue-deadlines.d.ts +14 -0
- package/dist/ports/venue-deadlines.d.ts.map +1 -0
- package/dist/ports/venue-deadlines.js +2 -0
- package/dist/ports/venue-deadlines.js.map +1 -0
- package/dist/presets.d.ts +12 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -11
- package/dist/presets.js.map +1 -1
- package/dist/submission.d.ts +8 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +41 -10
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +3 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +19 -0
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +19 -0
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +69 -0
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/venue-rules.d.ts +10 -0
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +26 -10
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +139 -11
- package/docs/rules/cycle/evidence.md +87 -0
- package/docs/rules/cycle/port-done.md +75 -0
- package/docs/rules/cycle/record.md +152 -0
- package/docs/rules/cycle/stage.md +85 -0
- package/docs/rules.md +48 -43
- package/docs/submission.md +5 -0
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint.config.mjs +41 -1
- package/lib/paper-config.ts +3 -0
- package/package.json +1 -1
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/msr.jsonc +25 -1
- package/presets/msr.md +5 -1
- package/presets/venue-profile.schema.json +39 -0
- package/scripts/check.ts +4 -0
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/src/adapters/hotcrp/cassette/README.md +22 -0
- package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
- package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
- package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
- package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
- package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
- package/src/adapters/hotcrp/deadlines.io.ts +27 -0
- package/src/adapters/hotcrp/deadlines.test.ts +170 -0
- package/src/adapters/hotcrp/deadlines.ts +161 -0
- package/src/adapters/hotcrp/index.ts +2 -0
- package/src/cli.test.ts +9 -0
- package/src/cli.ts +43 -4
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/cycle.test.ts +456 -0
- package/src/domain/cycle.ts +432 -0
- package/src/domain/deadline.test.ts +386 -0
- package/src/domain/deadline.ts +472 -0
- package/src/new-paper.test.ts +1 -0
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/presets.test.ts +105 -0
- package/src/presets.ts +43 -10
- package/src/rule-cases.test.ts +144 -0
- package/src/submission.test.ts +105 -1
- package/src/submission.ts +73 -12
- package/src/tex-requirements.ts +31 -0
- package/src/tex-venue-rules.test.ts +76 -0
- package/src/tex-venue-rules.ts +85 -0
- package/src/venue-rules.test.ts +82 -0
- package/src/venue-rules.ts +44 -12
package/eslint.config.mjs
CHANGED
|
@@ -413,6 +413,40 @@ export const IO_GLOBALS = {
|
|
|
413
413
|
},
|
|
414
414
|
};
|
|
415
415
|
|
|
416
|
+
/**
|
|
417
|
+
* `paperlint lint` NEVER READS THE CLOCK. Its findings are a function of the files and the installed
|
|
418
|
+
* package: the same checkout lints the same tomorrow, and a deadline's distance belongs to a status
|
|
419
|
+
* view or a calendar, not to a gate (a lint that turns red on an untouched tree gets muted). The
|
|
420
|
+
* clock is an effect like the disk, so it is read where effects are — an adapter's `*.io.ts` and the
|
|
421
|
+
* root, `src/cli.ts` — and passed in. Matched on the AST: `Date.now` (called or passed along),
|
|
422
|
+
* `new Date()` and `Date()` without an argument. `new Date(ms)` converts a given time and passes.
|
|
423
|
+
* `toolchain.ts` and `build-engine.ts` keep `Date.now` as a default for their own timing (build and
|
|
424
|
+
* install only, neither runs under `lint`); that legacy I/O moves behind a port in #76.
|
|
425
|
+
*/
|
|
426
|
+
const READS_THE_CLOCK = [
|
|
427
|
+
"MemberExpression[object.name='Date'][property.name='now']",
|
|
428
|
+
"NewExpression[callee.name='Date'][arguments.length=0]",
|
|
429
|
+
"CallExpression[callee.name='Date']",
|
|
430
|
+
].map((selector) => ({
|
|
431
|
+
selector,
|
|
432
|
+
message:
|
|
433
|
+
"Lint never reads the clock: its findings are a function of the files. Take the time as an argument; read it in an adapter's *.io.ts or src/cli.ts.",
|
|
434
|
+
}));
|
|
435
|
+
|
|
436
|
+
/** Every source file the checks are made of, but the places effects are allowed. Same scope as axis B. */
|
|
437
|
+
export const NO_CLOCK = {
|
|
438
|
+
files: ["src/**/*.ts"],
|
|
439
|
+
ignores: [
|
|
440
|
+
...IO_GLOBALS.ignores,
|
|
441
|
+
// Legacy I/O, moves behind a port in #76 (see READS_THE_CLOCK).
|
|
442
|
+
"src/toolchain.ts",
|
|
443
|
+
"src/build-engine.ts",
|
|
444
|
+
],
|
|
445
|
+
rules: {
|
|
446
|
+
"no-restricted-syntax": ["error", AS_UNKNOWN_AS, ...READS_THE_CLOCK],
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
|
|
416
450
|
/**
|
|
417
451
|
* EVERY PRESET HAS A CARD, AND EVERY CARD'S RULES TABLE IS THE PRESET'S. Two checks, one block each:
|
|
418
452
|
*
|
|
@@ -608,6 +642,7 @@ export default [
|
|
|
608
642
|
},
|
|
609
643
|
// Hexagonal layers, both axes (src/CLAUDE.md): knowledge by element, purity by file category.
|
|
610
644
|
IO_GLOBALS,
|
|
645
|
+
NO_CLOCK,
|
|
611
646
|
layerBoundaries(dirname(fileURLToPath(import.meta.url))),
|
|
612
647
|
// The ratchet: per-file ceilings for the files written before the limits existed. See RATCHET.
|
|
613
648
|
...Object.entries(RATCHET).map(([file, max]) => ({
|
|
@@ -805,7 +840,12 @@ export default [
|
|
|
805
840
|
files: ["eslint-rules/*.ts", "eslint-rules/*.mjs"],
|
|
806
841
|
ignores: ["eslint-rules/*.test.*", "eslint-rules/*.harness.*"],
|
|
807
842
|
rules: {
|
|
808
|
-
"no-restricted-syntax": [
|
|
843
|
+
"no-restricted-syntax": [
|
|
844
|
+
"error",
|
|
845
|
+
AS_UNKNOWN_AS,
|
|
846
|
+
...GIT_IN_A_RULE,
|
|
847
|
+
...READS_THE_CLOCK,
|
|
848
|
+
],
|
|
809
849
|
},
|
|
810
850
|
},
|
|
811
851
|
// LAST, so no block above can replace its `no-restricted-syntax` options for these files (none
|
package/lib/paper-config.ts
CHANGED
|
@@ -143,6 +143,8 @@ export const SETTINGS_KEYS: Readonly<Record<string, string>> = Object.freeze({
|
|
|
143
143
|
talk: 'how this paper is presented: { "mode": "remote-video", "dir"?, "files"?, "one_slide"? } — the talk/ rules. A paper\'s own; refused in the root file',
|
|
144
144
|
submission:
|
|
145
145
|
"which submission on the venue's portal is this paper's: { \"id\": <number> } — paperlint submission, with the preset's \"portal\". A paper's own; refused in the root file",
|
|
146
|
+
cycles:
|
|
147
|
+
"the paper's attempts at venues, oldest first: [{ id, venue, kind, opened, phase?, deadlines, submission?, outcome }] — extends, kind and submission are then the current attempt's, and the cycle/ rules judge the record. A paper's own; refused in the root file",
|
|
146
148
|
scripts: "how skill prose names the scripts — consumer.mjs",
|
|
147
149
|
timezone: "the zone deadline anchors are written in — consumer.mjs",
|
|
148
150
|
contactEmail: "the polite-pool address for citation lookups — consumer.mjs",
|
|
@@ -155,6 +157,7 @@ export const SETTINGS_KEYS: Readonly<Record<string, string>> = Object.freeze({
|
|
|
155
157
|
export const PAPER_ONLY_KEYS: readonly string[] = Object.freeze([
|
|
156
158
|
"talk",
|
|
157
159
|
"submission",
|
|
160
|
+
"cycles",
|
|
158
161
|
]);
|
|
159
162
|
|
|
160
163
|
/** The keys that only mean something for the whole project: refused in a paper's `paperlint.json`. */
|
package/package.json
CHANGED
package/presets/aidc.jsonc
CHANGED
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
// and appendices.»
|
|
14
14
|
// «Submissions must be properly anonymized for double-blind review.»
|
|
15
15
|
//
|
|
16
|
-
// THE DEADLINE PEOPLE GET WRONG
|
|
16
|
+
// THE DEADLINE PEOPLE GET WRONG: the HotCRP portal closed 2026-10-02 11:59:59 UTC first
|
|
17
17
|
// (`"sub":1790942399` on https://aidc.submit.acsac.org/deadlines, recorded in paperlint#90), while
|
|
18
|
-
// the workshop page
|
|
19
|
-
// what refuses the upload
|
|
18
|
+
// the workshop page said «October 2, 2026 (Anywhere-on-Earth, AoE)» — a day later; by 2026-10-05
|
|
19
|
+
// the portal had moved it a day (`"sub":1791028799`). The portal is what refuses the upload, so its
|
|
20
|
+
// readings are `deadlines` below, rewritten by the scheduled refresh in paperlint's repository
|
|
21
|
+
// (.github/workflows/deadlines.yml) when the portal changes them.
|
|
20
22
|
//
|
|
21
23
|
// THE REVIEW IS DOUBLE-BLIND: `"blind":true` in the same portal data, and the call above.
|
|
22
24
|
//
|
|
@@ -38,6 +40,25 @@
|
|
|
38
40
|
// the HotCRP API, there.
|
|
39
41
|
"portal": { "kind": "hotcrp", "url": "https://aidc.submit.acsac.org" },
|
|
40
42
|
|
|
43
|
+
// Read from the portal's public deadlines page; the camera-ready only in its status object
|
|
44
|
+
// (`"final":{"done":1793447999}`).
|
|
45
|
+
"deadlines": [
|
|
46
|
+
{
|
|
47
|
+
"what": "submission",
|
|
48
|
+
"at": "2026-10-03T11:59:59Z",
|
|
49
|
+
"source": "portal",
|
|
50
|
+
"url": "https://aidc.submit.acsac.org/deadlines",
|
|
51
|
+
"read": "2026-10-05",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"what": "camera-ready",
|
|
55
|
+
"at": "2026-10-31T11:59:59Z",
|
|
56
|
+
"source": "portal",
|
|
57
|
+
"url": "https://aidc.submit.acsac.org/deadlines",
|
|
58
|
+
"read": "2026-10-05",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
|
|
41
62
|
// What the venue is called in a paper's text (tex/venue-leftover): the workshop, and the
|
|
42
63
|
// conference it is held at.
|
|
43
64
|
"aliases": ["AIDC", "ACSAC"],
|
package/presets/aidc.md
CHANGED
|
@@ -21,9 +21,12 @@ from it. The `submit-paper` skill reads this card for the venue's specifics.
|
|
|
21
21
|
- **Double-blind**: «Submissions must be properly anonymized for double-blind review.», and
|
|
22
22
|
`"blind":true` in the portal's data.
|
|
23
23
|
- **Portal**: HotCRP at `https://aidc.submit.acsac.org`.
|
|
24
|
-
- 🔴 **The deadline people get wrong**: the HotCRP portal
|
|
25
|
-
(`"sub":1790942399` on `https://aidc.submit.acsac.org/deadlines`), while the workshop page
|
|
26
|
-
«October 2, 2026 (Anywhere-on-Earth, AoE)» — a day later
|
|
24
|
+
- 🔴 **The deadline people get wrong**: the HotCRP portal closed 2026-10-02 11:59:59 UTC first
|
|
25
|
+
(`"sub":1790942399` on `https://aidc.submit.acsac.org/deadlines`), while the workshop page said
|
|
26
|
+
«October 2, 2026 (Anywhere-on-Earth, AoE)» — a day later; on 2026-10-05 the portal read
|
|
27
|
+
2026-10-03 11:59:59 UTC (`"sub":1791028799`). The portal is what refuses the upload. The preset
|
|
28
|
+
carries its readings as `deadlines`, refreshed by paperlint's scheduled workflow when the portal
|
|
29
|
+
changes them.
|
|
27
30
|
|
|
28
31
|
## Paper kinds and limits
|
|
29
32
|
|
package/presets/msr.jsonc
CHANGED
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
// camera-ready version.»
|
|
16
16
|
// «The MSR 2027 Technical Track will employ a double-anonymous review process.»
|
|
17
17
|
//
|
|
18
|
-
//
|
|
18
|
+
// THE DEADLINES ARE THE PORTAL'S, as read: `deadlines` below is rewritten by the scheduled refresh
|
|
19
|
+
// in paperlint's repository (.github/workflows/deadlines.yml) when the portal changes them, and a
|
|
20
|
+
// paper reaches the new dates through its normal update of paperlint. The call's own wording
|
|
21
|
+
// (abstract and paper days, AoE) is in the card, msr.md beside this file.
|
|
19
22
|
{
|
|
20
23
|
"type": "venue",
|
|
21
24
|
"name": "MSR",
|
|
@@ -34,6 +37,27 @@
|
|
|
34
37
|
// token — the HotCRP API, there.
|
|
35
38
|
"portal": { "kind": "hotcrp", "url": "https://msr2027.hotcrp.com" },
|
|
36
39
|
|
|
40
|
+
// Read from the portal's public deadlines page (no account) by the adapter in src/adapters/hotcrp/.
|
|
41
|
+
// «Submissions must be completed by this deadline» is the `submission`: on the CALL's abstract
|
|
42
|
+
// day, 04:00 UTC — four days before the call's paper day. «Completed submissions may be updated
|
|
43
|
+
// until this deadline» is the `resubmission`.
|
|
44
|
+
"deadlines": [
|
|
45
|
+
{
|
|
46
|
+
"what": "submission",
|
|
47
|
+
"at": "2026-10-20T04:00:00Z",
|
|
48
|
+
"source": "portal",
|
|
49
|
+
"url": "https://msr2027.hotcrp.com/deadlines",
|
|
50
|
+
"read": "2026-10-05",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"what": "resubmission",
|
|
54
|
+
"at": "2026-10-23T04:00:00Z",
|
|
55
|
+
"source": "portal",
|
|
56
|
+
"url": "https://msr2027.hotcrp.com/deadlines",
|
|
57
|
+
"read": "2026-10-05",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
|
|
37
61
|
// What the venue is called in a paper's text (tex/venue-leftover) — and only beside a year
|
|
38
62
|
// («MSR 2027», «MSR'27»): «MSR» is also Microsoft Research, and an acronym papers define for
|
|
39
63
|
// themselves («Manual Speech Recognition (MSR)», in an accepted ACSAC paper of
|
package/presets/msr.md
CHANGED
|
@@ -28,7 +28,11 @@ whereas last year it was ACM format.»), and limits and dates change between edi
|
|
|
28
28
|
(`"sub":1792468800`, 2026-10-20 04:00 UTC) and «Resubmission deadline: Friday Oct 23, 2026, 12 AM
|
|
29
29
|
EDT» («Completed submissions may be updated until this deadline»). Midnight EDT at the start of
|
|
30
30
|
each day is about a day and a half before the end of that day AoE. The portal is what refuses the
|
|
31
|
-
upload: plan for its times
|
|
31
|
+
upload: plan for its times. The preset carries them as `deadlines` (`source: "portal"`, with the
|
|
32
|
+
day read), refreshed by paperlint's scheduled workflow when the portal changes them; a paper whose
|
|
33
|
+
current cycle names `paperlint:msr` derives them. Recording the call's days in the paper's own
|
|
34
|
+
cycle maps «abstract» to `registration` and «paper» to `submission` — and the portal's earlier
|
|
35
|
+
`submission` is then the one in force.
|
|
32
36
|
- **Format**, verbatim from the call: «LaTeX users must use \documentclass[10pt,conference]{IEEEtran}
|
|
33
37
|
without including the compsoc or compsocconf options», «title in 24pt font and full text in 10pt
|
|
34
38
|
type», and «Alterations of spacing, font size, and other changes that deviate from the
|
|
@@ -78,6 +78,45 @@
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
+
"deadlines": {
|
|
82
|
+
"description": "The edition's deadlines as they were READ — the portal's readings, rewritten by the scheduled refresh in paperlint's repository when the portal changes them (a patch release), each with the page and the day it was read. A paper whose current cycle names this preset derives them; its own record adds the call's readings and overrides on top, and the earlier reading binds. `what`: registration (abstract registration — a call's \"abstract\" date), submission (the full paper), resubmission (updates to a completed submission), notification, camera-ready. At most one reading per kind and source. A child's list replaces its parent's.",
|
|
83
|
+
"type": "array",
|
|
84
|
+
"items": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"required": ["what", "at", "source", "url", "read"],
|
|
88
|
+
"properties": {
|
|
89
|
+
"what": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"enum": [
|
|
92
|
+
"registration",
|
|
93
|
+
"submission",
|
|
94
|
+
"resubmission",
|
|
95
|
+
"notification",
|
|
96
|
+
"camera-ready"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"at": {
|
|
100
|
+
"description": "An instant in UTC, as the portal publishes it: 2026-10-20T04:00:00Z.",
|
|
101
|
+
"type": "string",
|
|
102
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
|
|
103
|
+
},
|
|
104
|
+
"source": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"enum": ["portal", "call"]
|
|
107
|
+
},
|
|
108
|
+
"url": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"pattern": "^https?://[^\\s/]+[^\\s]*$"
|
|
111
|
+
},
|
|
112
|
+
"read": {
|
|
113
|
+
"description": "The day the page was read, YYYY-MM-DD.",
|
|
114
|
+
"type": "string",
|
|
115
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
81
120
|
"required_sections": {
|
|
82
121
|
"description": "Sections the venue requires, read by `tex/required-section`. Each is a `\\section` or `\\section*` whose title equals `title` once whitespace is collapsed. `position: \"last\"`: after every section of the body (the ones before the appendix and the bibliography); it may stand after the bibliography. A child preset's list replaces its parent's.",
|
|
83
122
|
"type": "array",
|
package/scripts/check.ts
CHANGED
|
@@ -202,6 +202,10 @@ export const NOT_COVERED: Readonly<Record<string, string>> = {
|
|
|
202
202
|
validate:
|
|
203
203
|
"pr-title.yml checks the pull request's TITLE (the squash commit semantic-release reads), " +
|
|
204
204
|
"which exists only on GitHub, not in the tree.",
|
|
205
|
+
refresh:
|
|
206
|
+
"deadlines.yml reads the venue presets' portals over the network once a day and opens a pull " +
|
|
207
|
+
"request when a deadline moved; npm test replays the recorded portal pages through " +
|
|
208
|
+
"scripts/refresh-deadlines.test.ts, and nothing local should read the live portals on every check.",
|
|
205
209
|
};
|
|
206
210
|
|
|
207
211
|
/**
|