@hominis/fireforge 0.51.0 → 0.53.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/CHANGELOG.md +46 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/bootstrap.js +2 -3
- package/dist/src/commands/build-prelock.d.ts +3 -3
- package/dist/src/commands/build-prelock.js +3 -3
- package/dist/src/commands/build.js +4 -0
- package/dist/src/commands/declarations-report.d.ts +9 -3
- package/dist/src/commands/declarations-report.js +18 -11
- package/dist/src/commands/discard.js +3 -3
- package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
- package/dist/src/commands/doctor-furnace.js +5 -5
- package/dist/src/commands/export-adopt-check.d.ts +5 -6
- package/dist/src/commands/export-adopt-check.js +5 -6
- package/dist/src/commands/export-flow.js +6 -9
- package/dist/src/commands/export-staged-registration.d.ts +4 -4
- package/dist/src/commands/export-staged-registration.js +4 -4
- package/dist/src/commands/forward-import-remedy.d.ts +2 -3
- package/dist/src/commands/forward-import-remedy.js +2 -3
- package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
- package/dist/src/commands/furnace/index.js +2 -2
- package/dist/src/commands/lint.js +4 -4
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
- package/dist/src/commands/patch/lint-ignore-from.js +3 -4
- package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
- package/dist/src/commands/patch/move-files-adopt.js +65 -0
- package/dist/src/commands/patch/move-files-create.js +41 -20
- package/dist/src/commands/patch/move-files-into.d.ts +18 -0
- package/dist/src/commands/patch/move-files-into.js +125 -60
- package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
- package/dist/src/commands/patch/move-files-retire.js +91 -0
- package/dist/src/commands/patch/move-files.d.ts +3 -0
- package/dist/src/commands/patch/move-files.js +42 -12
- package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
- package/dist/src/commands/patch/move-size-tier.js +66 -0
- package/dist/src/commands/patch/rename.js +1 -1
- package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
- package/dist/src/commands/patch/reorder-projection.js +49 -0
- package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
- package/dist/src/commands/patch/reorder-shift.js +307 -0
- package/dist/src/commands/patch/reorder.d.ts +9 -15
- package/dist/src/commands/patch/reorder.js +52 -50
- package/dist/src/commands/patch/split-plan.d.ts +31 -3
- package/dist/src/commands/patch/split-plan.js +36 -12
- package/dist/src/commands/patch/split-staged.d.ts +2 -2
- package/dist/src/commands/patch/split-staged.js +2 -2
- package/dist/src/commands/patch/split.js +18 -5
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
- package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
- package/dist/src/commands/patch/staged-dependency-from.js +15 -9
- package/dist/src/commands/patch/staged-dependency.js +13 -9
- package/dist/src/commands/patch/tier.js +2 -2
- package/dist/src/commands/patch-rename-map.d.ts +14 -3
- package/dist/src/commands/patch-rename-map.js +21 -3
- package/dist/src/commands/re-export-adjacent.d.ts +4 -5
- package/dist/src/commands/re-export-lint.d.ts +4 -3
- package/dist/src/commands/re-export-lint.js +4 -3
- package/dist/src/commands/re-export-preflight.js +1 -2
- package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
- package/dist/src/commands/re-export-purity-scope.js +13 -7
- package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
- package/dist/src/commands/re-export-retired-declarations.js +113 -0
- package/dist/src/commands/re-export-single.js +2 -2
- package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
- package/dist/src/commands/re-export-stale-registrations.js +65 -21
- package/dist/src/commands/re-export.js +4 -0
- package/dist/src/commands/rebase/continue.js +3 -3
- package/dist/src/commands/register.js +5 -4
- package/dist/src/commands/status-output.js +5 -1
- package/dist/src/commands/status.js +1 -1
- package/dist/src/commands/test-build-furnace.d.ts +1 -2
- package/dist/src/commands/test-build-furnace.js +1 -2
- package/dist/src/commands/test-build-phase.d.ts +10 -0
- package/dist/src/commands/test-build-phase.js +51 -16
- package/dist/src/commands/test-control-restore.d.ts +40 -0
- package/dist/src/commands/test-control-restore.js +86 -0
- package/dist/src/commands/test-dispatch.js +83 -13
- package/dist/src/commands/test-multi-verdict.js +1 -0
- package/dist/src/commands/test-orchestrate.d.ts +11 -0
- package/dist/src/commands/test-orchestrate.js +33 -2
- package/dist/src/commands/test-perf-scope.d.ts +1 -1
- package/dist/src/commands/test-perf-scope.js +1 -1
- package/dist/src/commands/test-prelock.d.ts +7 -8
- package/dist/src/commands/test-prelock.js +15 -12
- package/dist/src/commands/test-register.js +2 -2
- package/dist/src/commands/test-results.d.ts +10 -1
- package/dist/src/commands/test-results.js +20 -3
- package/dist/src/commands/test-run.d.ts +6 -0
- package/dist/src/commands/test-run.js +23 -4
- package/dist/src/commands/test-shards.d.ts +7 -3
- package/dist/src/commands/test-shards.js +24 -7
- package/dist/src/commands/test-stale-gate.d.ts +6 -1
- package/dist/src/commands/test-stale-gate.js +47 -2
- package/dist/src/commands/test-verdict.d.ts +5 -3
- package/dist/src/commands/test.js +14 -1
- package/dist/src/commands/token.js +2 -2
- package/dist/src/commands/verify.js +1 -1
- package/dist/src/commands/watch.js +1 -1
- package/dist/src/commands/wire.js +1 -1
- package/dist/src/core/build-audit-resolve.d.ts +4 -5
- package/dist/src/core/build-audit-resolve.js +4 -5
- package/dist/src/core/build-audit-skip.d.ts +2 -2
- package/dist/src/core/build-audit.d.ts +3 -4
- package/dist/src/core/build-baseline-types.d.ts +37 -3
- package/dist/src/core/build-baseline-validate.d.ts +30 -1
- package/dist/src/core/build-baseline-validate.js +134 -33
- package/dist/src/core/build-baseline.d.ts +8 -2
- package/dist/src/core/build-baseline.js +32 -16
- package/dist/src/core/build-jar-escalation.d.ts +4 -4
- package/dist/src/core/build-jar-escalation.js +4 -4
- package/dist/src/core/build-prepare.js +4 -4
- package/dist/src/core/config-validate.js +4 -4
- package/dist/src/core/coverage-extend.d.ts +5 -3
- package/dist/src/core/coverage-extend.js +8 -3
- package/dist/src/core/coverage-records.d.ts +4 -5
- package/dist/src/core/coverage-records.js +4 -5
- package/dist/src/core/destructive.d.ts +2 -2
- package/dist/src/core/destructive.js +2 -2
- package/dist/src/core/discard-baseline.d.ts +2 -2
- package/dist/src/core/discard-baseline.js +3 -3
- package/dist/src/core/engine-session-lock.d.ts +1 -1
- package/dist/src/core/engine-session-lock.js +1 -1
- package/dist/src/core/file-lock-owner.d.ts +5 -5
- package/dist/src/core/file-lock-owner.js +5 -5
- package/dist/src/core/file-lock-queue.js +6 -8
- package/dist/src/core/file-lock-shared.d.ts +4 -4
- package/dist/src/core/file-lock-shared.js +4 -4
- package/dist/src/core/file-lock.js +7 -10
- package/dist/src/core/firefox.js +2 -2
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
- package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
- package/dist/src/core/furnace-apply.js +3 -3
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +4 -3
- package/dist/src/core/furnace-config-validate-exceptions.js +26 -6
- package/dist/src/core/furnace-operation.js +5 -5
- package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
- package/dist/src/core/furnace-registration-rename.d.ts +5 -5
- package/dist/src/core/furnace-registration-rename.js +5 -5
- package/dist/src/core/furnace-registration-validate.d.ts +2 -1
- package/dist/src/core/furnace-registration-validate.js +2 -1
- package/dist/src/core/furnace-registration.js +2 -2
- package/dist/src/core/furnace-validate-accessibility.d.ts +1 -1
- package/dist/src/core/furnace-validate-accessibility.js +22 -11
- package/dist/src/core/furnace-validate-exceptions.d.ts +2 -1
- package/dist/src/core/furnace-validate-exceptions.js +31 -13
- package/dist/src/core/git-base.js +4 -3
- package/dist/src/core/git-diff.js +1 -1
- package/dist/src/core/git.js +1 -1
- package/dist/src/core/harness-orphans.d.ts +6 -6
- package/dist/src/core/harness-orphans.js +6 -6
- package/dist/src/core/harness-port-reservation.d.ts +60 -0
- package/dist/src/core/harness-port-reservation.js +122 -0
- package/dist/src/core/history-log-file.d.ts +9 -9
- package/dist/src/core/history-log-file.js +15 -15
- package/dist/src/core/history-log.d.ts +3 -3
- package/dist/src/core/history-log.js +3 -3
- package/dist/src/core/launchable-build.d.ts +39 -0
- package/dist/src/core/launchable-build.js +85 -0
- package/dist/src/core/marionette-port.js +1 -3
- package/dist/src/core/moz-manifest-rules.js +3 -2
- package/dist/src/core/negative-control-compiled.d.ts +22 -3
- package/dist/src/core/negative-control-compiled.js +41 -5
- package/dist/src/core/parser-fallback.d.ts +5 -5
- package/dist/src/core/parser-fallback.js +5 -5
- package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
- package/dist/src/core/patch-identifier-suggest.js +11 -4
- package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
- package/dist/src/core/patch-lint-checkjs.js +1 -1
- package/dist/src/core/patch-lint-css.js +2 -2
- package/dist/src/core/patch-lint-file-size.d.ts +5 -7
- package/dist/src/core/patch-lint-file-size.js +5 -7
- package/dist/src/core/patch-lint-ignore.d.ts +3 -3
- package/dist/src/core/patch-lint-ignore.js +3 -3
- package/dist/src/core/patch-lint-prettier.d.ts +4 -6
- package/dist/src/core/patch-lint-prettier.js +4 -6
- package/dist/src/core/patch-lint-projection.d.ts +1 -1
- package/dist/src/core/patch-lint-projection.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
- package/dist/src/core/patch-lint-queue-types.js +2 -2
- package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
- package/dist/src/core/patch-manifest-consistency.js +2 -2
- package/dist/src/core/patch-manifest-io.d.ts +5 -1
- package/dist/src/core/patch-manifest-io.js +12 -10
- package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
- package/dist/src/core/patch-manifest-key-order.js +3 -4
- package/dist/src/core/patch-parse.d.ts +3 -3
- package/dist/src/core/patch-parse.js +3 -3
- package/dist/src/core/patch-policy.d.ts +1 -1
- package/dist/src/core/patch-policy.js +1 -1
- package/dist/src/core/patch-source-metadata.d.ts +1 -1
- package/dist/src/core/patch-source-metadata.js +1 -1
- package/dist/src/core/patch-subset.d.ts +5 -3
- package/dist/src/core/patch-subset.js +8 -7
- package/dist/src/core/patch-transform.d.ts +2 -2
- package/dist/src/core/patch-transform.js +2 -2
- package/dist/src/core/rebase-session.js +4 -6
- package/dist/src/core/register-xpcshell-test.d.ts +28 -7
- package/dist/src/core/register-xpcshell-test.js +76 -27
- package/dist/src/core/run-log.d.ts +8 -8
- package/dist/src/core/run-log.js +8 -8
- package/dist/src/core/status-classify.d.ts +1 -2
- package/dist/src/core/status-classify.js +1 -2
- package/dist/src/core/test-harness-crash.d.ts +5 -3
- package/dist/src/core/test-harness-crash.js +13 -14
- package/dist/src/core/test-harness-infra.d.ts +32 -0
- package/dist/src/core/test-harness-infra.js +69 -0
- package/dist/src/core/test-harness-signature.d.ts +6 -0
- package/dist/src/core/test-run-completeness.d.ts +12 -5
- package/dist/src/core/test-run-completeness.js +15 -8
- package/dist/src/core/test-stale-check.d.ts +48 -1
- package/dist/src/core/test-stale-check.js +97 -2
- package/dist/src/core/test-stall-triage.js +5 -7
- package/dist/src/core/token-category.js +2 -2
- package/dist/src/core/token-inventory.js +3 -3
- package/dist/src/core/token-manager.d.ts +2 -2
- package/dist/src/core/token-manager.js +5 -7
- package/dist/src/core/token-variant.d.ts +6 -6
- package/dist/src/core/token-variant.js +7 -7
- package/dist/src/core/typecheck-shim.d.ts +2 -1
- package/dist/src/core/typecheck-shim.js +29 -5
- package/dist/src/core/wire-destroy.d.ts +1 -1
- package/dist/src/core/wire-destroy.js +1 -1
- package/dist/src/core/wire-dom-fragment.js +1 -1
- package/dist/src/core/wire-init.d.ts +1 -1
- package/dist/src/core/wire-init.js +1 -1
- package/dist/src/core/wire-subscript.d.ts +1 -1
- package/dist/src/core/wire-subscript.js +1 -1
- package/dist/src/core/wire-utils.d.ts +2 -2
- package/dist/src/core/wire-utils.js +3 -3
- package/dist/src/errors/base.d.ts +1 -1
- package/dist/src/errors/base.js +1 -1
- package/dist/src/errors/rebase.d.ts +1 -1
- package/dist/src/errors/rebase.js +1 -1
- package/dist/src/types/commands/options.d.ts +26 -3
- package/dist/src/types/config.d.ts +5 -7
- package/dist/src/types/furnace.d.ts +9 -0
- package/dist/src/utils/options.js +2 -2
- package/dist/src/utils/process.js +2 -2
- package/dist/src/utils/regex.d.ts +2 -2
- package/dist/src/utils/regex.js +3 -3
- package/dist/src/utils/sleep.d.ts +5 -6
- package/dist/src/utils/sleep.js +5 -6
- package/dist/src/utils/validation.js +2 -2
- package/docs/configuration.md +40 -26
- package/docs/exit-codes.md +8 -11
- package/docs/furnace.md +21 -3
- package/docs/lifecycle-invariants.md +36 -45
- package/docs/machine-output.md +43 -22
- package/docs/patch-workflow.md +129 -24
- package/docs/run-logs.md +8 -9
- package/docs/testing.md +118 -44
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ import { findVariantBlockDeclaration, insertVariantDeclaration, validateVariantS
|
|
|
17
17
|
*
|
|
18
18
|
* Derived from {@link TOKEN_MODES} so the runtime allowlist and the type
|
|
19
19
|
* cannot drift: adding a member here without updating the list (or the
|
|
20
|
-
* reverse) is
|
|
20
|
+
* reverse) is impossible, because there is only one list.
|
|
21
21
|
*/
|
|
22
22
|
export const TOKEN_MODES = ['auto', 'static', 'override'];
|
|
23
23
|
/** Narrows an arbitrary string to a {@link TokenMode}. */
|
|
@@ -109,12 +109,10 @@ function normalizeVariantOption(options) {
|
|
|
109
109
|
* Narrows {@link AddTokenOptions.category} for the base-declaration path,
|
|
110
110
|
* which cannot proceed without one.
|
|
111
111
|
*
|
|
112
|
-
* `--category`
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* a fair goal that produced a mandatory argument describing nothing. Under
|
|
117
|
-
* a variant it is now optional. Everywhere else this states the reason
|
|
112
|
+
* `--category` is optional under `--variant`, where the declaration is
|
|
113
|
+
* routed into a `:root<selector>` block and the category names nothing
|
|
114
|
+
* about where the token lands, so requiring it there would make a
|
|
115
|
+
* mandatory argument describing nothing. Everywhere else this states the reason
|
|
118
116
|
* rather than leaving commander to print a bare "required option not
|
|
119
117
|
* specified".
|
|
120
118
|
*
|
|
@@ -28,12 +28,12 @@ export type VariantValidation = {
|
|
|
28
28
|
* Boolean-attribute fragments are returned unchanged.
|
|
29
29
|
*
|
|
30
30
|
* Structure is decided by {@link rootAttributeSelector}, the matcher's own
|
|
31
|
-
* parse, rather than by a second regex.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `:root[data-skin="humanist"]:not([data-private])`
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* parse, rather than by a second regex. The matcher consumes consecutive
|
|
32
|
+
* `[…]` groups and stops at a pseudo-class tail, and a separate validator
|
|
33
|
+
* that does not follow it leaves blocks such as
|
|
34
|
+
* `:root[data-skin="humanist"]:not([data-private])` findable but
|
|
35
|
+
* unnameable. Running both halves off one parse keeps them from drifting
|
|
36
|
+
* apart. Character-level safety stays a
|
|
37
37
|
* separate, per-group check, because the matcher finds bracket boundaries
|
|
38
38
|
* and validates no characters at all.
|
|
39
39
|
*
|
|
@@ -68,12 +68,12 @@ function normalizeAttributeFragment(group) {
|
|
|
68
68
|
* Boolean-attribute fragments are returned unchanged.
|
|
69
69
|
*
|
|
70
70
|
* Structure is decided by {@link rootAttributeSelector}, the matcher's own
|
|
71
|
-
* parse, rather than by a second regex.
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* `:root[data-skin="humanist"]:not([data-private])`
|
|
75
|
-
*
|
|
76
|
-
*
|
|
71
|
+
* parse, rather than by a second regex. The matcher consumes consecutive
|
|
72
|
+
* `[…]` groups and stops at a pseudo-class tail, and a separate validator
|
|
73
|
+
* that does not follow it leaves blocks such as
|
|
74
|
+
* `:root[data-skin="humanist"]:not([data-private])` findable but
|
|
75
|
+
* unnameable. Running both halves off one parse keeps them from drifting
|
|
76
|
+
* apart. Character-level safety stays a
|
|
77
77
|
* separate, per-group check, because the matcher finds bracket boundaries
|
|
78
78
|
* and validates no characters at all.
|
|
79
79
|
*
|
|
@@ -194,7 +194,7 @@ function findVariantBlock(lines, variant) {
|
|
|
194
194
|
if (selector === undefined || canonicalSelector(selector.attributes) !== want)
|
|
195
195
|
continue;
|
|
196
196
|
// An unqualified request still matches a qualified block: that is the
|
|
197
|
-
//
|
|
197
|
+
// behaviour `variantBlockQualifier` exists to report, since
|
|
198
198
|
// silently skipping the block is the worse failure. A request that names
|
|
199
199
|
// a qualifier, though, means it, so it must match.
|
|
200
200
|
if (wantQualifier !== '' && canonicalSelector(selector.qualifier) !== wantQualifier)
|
|
@@ -82,7 +82,8 @@ export interface ComposedShim {
|
|
|
82
82
|
* shim path
|
|
83
83
|
* @param extraShimPath - Optional project-relative path to an extra `.d.ts`
|
|
84
84
|
* @param options - `litTypes` appends {@link LIT_TYPES_SHIM} after the
|
|
85
|
-
* built-in shim (before the extra shim, which can still merge into it)
|
|
85
|
+
* built-in shim (before the extra shim, which can still merge into it),
|
|
86
|
+
* and {@link LOOSE_LIT_SHIM} otherwise
|
|
86
87
|
*/
|
|
87
88
|
export declare function composeShimSource(projectRoot: string, extraShimPath: string | undefined, options?: {
|
|
88
89
|
litTypes?: boolean | undefined;
|
|
@@ -291,12 +291,17 @@ export const UNDEFINED_IDENTIFIER_CODES = new Set([
|
|
|
291
291
|
]);
|
|
292
292
|
/**
|
|
293
293
|
* Typed Lit, opt-in through `typecheck.litTypes`. Declares the two chrome
|
|
294
|
-
* modules widgets import Lit from, so they
|
|
294
|
+
* modules widgets import Lit from, so they do not fall to the
|
|
295
295
|
* `chrome:*` wildcard as `any`: `LitElement` (with `ReactiveElement`'s
|
|
296
296
|
* lifecycle) and `MozLitElement` carry closed member lists, so a misspelt
|
|
297
297
|
* member of a widget host is TS2339, as `Cu` is in the built-in shim.
|
|
298
|
-
* Template helpers and directives stay loose.
|
|
299
|
-
*
|
|
298
|
+
* Template helpers and directives stay loose. The global
|
|
299
|
+
* `MozLitElementWith<Extra>` types a widget's query getters and
|
|
300
|
+
* `static properties` members: the widget casts its `MozLitElement` import
|
|
301
|
+
* to it before extending ({@link LOOSE_LIT_SHIM} keeps it `any` without
|
|
302
|
+
* typed Lit). A project
|
|
303
|
+
* shim can merge members into `LitElementShimMembers`; a project whose
|
|
304
|
+
* jsconfig `paths`
|
|
300
305
|
* maps these URLs to the real sources gets those instead, since a resolved
|
|
301
306
|
* file wins over an ambient module.
|
|
302
307
|
*/
|
|
@@ -399,6 +404,24 @@ declare module 'chrome://global/content/lit-utils.mjs' {
|
|
|
399
404
|
export const SelectControlBaseElement: any;
|
|
400
405
|
export const SelectControlItemMixin: any;
|
|
401
406
|
}
|
|
407
|
+
// MozLitElement with instance members Extra: what a widget casts its base
|
|
408
|
+
// to for the getters static queries installs (and properties it declares
|
|
409
|
+
// only in static properties). A class field would redefine a query getter
|
|
410
|
+
// and throw, and a constructor declaration reads it before renderRoot.
|
|
411
|
+
type MozLitElementWith<Extra> = Pick<
|
|
412
|
+
typeof import('chrome://global/content/lit-utils.mjs').MozLitElement,
|
|
413
|
+
keyof typeof import('chrome://global/content/lit-utils.mjs').MozLitElement
|
|
414
|
+
> &
|
|
415
|
+
(new () => import('chrome://global/content/lit-utils.mjs').MozLitElement & Extra);
|
|
416
|
+
`;
|
|
417
|
+
/**
|
|
418
|
+
* Without typed Lit, `MozLitElementWith<Extra>` is `any`, as the base it
|
|
419
|
+
* casts is: the same widget source checks under per-patch checkJs and a
|
|
420
|
+
* project typecheck that leaves `litTypes` off.
|
|
421
|
+
*/
|
|
422
|
+
const LOOSE_LIT_SHIM = `
|
|
423
|
+
// FireForge loose Lit (typecheck.litTypes off): see typecheck-shim.ts.
|
|
424
|
+
type MozLitElementWith<Extra> = any;
|
|
402
425
|
`;
|
|
403
426
|
/** Hint appended to reported undefined-identifier diagnostics. */
|
|
404
427
|
export const UNDEFINED_IDENTIFIER_HINT = '(undefined identifier — import or declare it, add the global to the extra shim, ' +
|
|
@@ -462,10 +485,11 @@ async function inlineTripleSlashReferences(source, baseDir, seen) {
|
|
|
462
485
|
* shim path
|
|
463
486
|
* @param extraShimPath - Optional project-relative path to an extra `.d.ts`
|
|
464
487
|
* @param options - `litTypes` appends {@link LIT_TYPES_SHIM} after the
|
|
465
|
-
* built-in shim (before the extra shim, which can still merge into it)
|
|
488
|
+
* built-in shim (before the extra shim, which can still merge into it),
|
|
489
|
+
* and {@link LOOSE_LIT_SHIM} otherwise
|
|
466
490
|
*/
|
|
467
491
|
export async function composeShimSource(projectRoot, extraShimPath, options = {}) {
|
|
468
|
-
const base = options.litTypes === true ?
|
|
492
|
+
const base = `${FIREFOX_GLOBALS_SHIM}\n${options.litTypes === true ? LIT_TYPES_SHIM : LOOSE_LIT_SHIM}`;
|
|
469
493
|
if (!extraShimPath) {
|
|
470
494
|
return { source: base, extraShimAppended: false };
|
|
471
495
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function addDestroyAST(content: string, expression: string, marker?: string): string;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Regex/line-based fallback implementation.
|
|
15
15
|
*/
|
|
16
16
|
export declare function legacyAddDestroy(content: string, expression: string, marker?: string): string;
|
|
17
17
|
/**
|
|
@@ -65,7 +65,7 @@ export function addDestroyAST(content, expression, marker = DEFAULT_MARKER) {
|
|
|
65
65
|
return ms.toString();
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Regex/line-based fallback implementation.
|
|
69
69
|
*/
|
|
70
70
|
export function legacyAddDestroy(content, expression, marker = DEFAULT_MARKER) {
|
|
71
71
|
const name = extractNameFromExpression(expression);
|
|
@@ -66,7 +66,7 @@ export async function probeDomFragmentInsertionPoint(engineDir, domFilePath, tar
|
|
|
66
66
|
// dry-run is allowed to proceed too.
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
-
// Check the tokenised and
|
|
69
|
+
// Check the tokenised and line-based insertion paths symmetrically with
|
|
70
70
|
// the real run. Either helper returning without throwing is sufficient
|
|
71
71
|
// evidence that the real run can land the directive.
|
|
72
72
|
addDomFragmentTokenized(content, includeDirective);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function addInitAST(content: string, expression: string, after?: string, marker?: string): string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Regex/line-based fallback implementation.
|
|
17
17
|
*/
|
|
18
18
|
export declare function legacyAddInit(content: string, expression: string, after?: string, marker?: string): string;
|
|
19
19
|
/**
|
|
@@ -112,7 +112,7 @@ export function addInitAST(content, expression, after, marker = DEFAULT_MARKER)
|
|
|
112
112
|
return ms.toString();
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* Regex/line-based fallback implementation.
|
|
116
116
|
*/
|
|
117
117
|
export function legacyAddInit(content, expression, after, marker = DEFAULT_MARKER) {
|
|
118
118
|
const name = extractNameFromExpression(expression);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function addSubscriptAST(content: string, name: string, marker?: string): string;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Regex/line-based fallback implementation.
|
|
14
14
|
*/
|
|
15
15
|
export declare function legacyAddSubscript(content: string, name: string, marker?: string): string;
|
|
16
16
|
/**
|
|
@@ -73,7 +73,7 @@ export function addSubscriptAST(content, name, marker = DEFAULT_MARKER) {
|
|
|
73
73
|
return ms.toString();
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* Regex/line-based fallback implementation.
|
|
77
77
|
*/
|
|
78
78
|
export function legacyAddSubscript(content, name, marker = DEFAULT_MARKER) {
|
|
79
79
|
const lines = content.split('\n');
|
|
@@ -104,7 +104,7 @@ export declare function walkToTryBlockEnd(lines: string[], startLine: number, op
|
|
|
104
104
|
}): number;
|
|
105
105
|
/**
|
|
106
106
|
* Scans the entire file and returns the net brace balance so callers can
|
|
107
|
-
* assert that a
|
|
107
|
+
* assert that a line-based fallback mutation did not silently introduce or
|
|
108
108
|
* drop a `{` / `}`. The helper reuses {@link countBraceDepth} so strings,
|
|
109
109
|
* comments, and regex literals are handled consistently with the walker.
|
|
110
110
|
*
|
|
@@ -117,7 +117,7 @@ export declare function computeFileBraceBalance(content: string): {
|
|
|
117
117
|
balanced: boolean;
|
|
118
118
|
};
|
|
119
119
|
/**
|
|
120
|
-
* Round-trip guard used after a
|
|
120
|
+
* Round-trip guard used after a line-based fallback mutation: if the file's
|
|
121
121
|
* net brace balance drifts between `before` and `after`, something went
|
|
122
122
|
* wrong and the fallback is refusing to write a corrupted file. Expects
|
|
123
123
|
* the delta to be exactly zero, since wire fallbacks only insert whole
|
|
@@ -159,7 +159,7 @@ export function tokenizeXhtml(lines) {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
// ---------------------------------------------------------------------------
|
|
162
|
-
//
|
|
162
|
+
// Line-based helpers, shared by fallback implementations in
|
|
163
163
|
// the wire target modules. Extracted to reduce duplication and make the brace-
|
|
164
164
|
// walking logic independently testable.
|
|
165
165
|
// ---------------------------------------------------------------------------
|
|
@@ -224,7 +224,7 @@ export function walkToTryBlockEnd(lines, startLine, options) {
|
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
226
226
|
* Scans the entire file and returns the net brace balance so callers can
|
|
227
|
-
* assert that a
|
|
227
|
+
* assert that a line-based fallback mutation did not silently introduce or
|
|
228
228
|
* drop a `{` / `}`. The helper reuses {@link countBraceDepth} so strings,
|
|
229
229
|
* comments, and regex literals are handled consistently with the walker.
|
|
230
230
|
*
|
|
@@ -244,7 +244,7 @@ export function computeFileBraceBalance(content) {
|
|
|
244
244
|
return { depth, balanced: depth === 0 };
|
|
245
245
|
}
|
|
246
246
|
/**
|
|
247
|
-
* Round-trip guard used after a
|
|
247
|
+
* Round-trip guard used after a line-based fallback mutation: if the file's
|
|
248
248
|
* net brace balance drifts between `before` and `after`, something went
|
|
249
249
|
* wrong and the fallback is refusing to write a corrupted file. Expects
|
|
250
250
|
* the delta to be exactly zero, since wire fallbacks only insert whole
|
|
@@ -80,7 +80,7 @@ export declare class InternalInvariantError extends FireForgeError {
|
|
|
80
80
|
get userMessage(): string;
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
|
-
* Raised when a
|
|
83
|
+
* Raised when a regex/brace-depth fallback parser decides it
|
|
84
84
|
* cannot safely perform its mutation, e.g. because a block it expected
|
|
85
85
|
* to walk never closes, because the inserted result fails a round-trip
|
|
86
86
|
* brace balance check, or because an expected pattern is missing.
|
package/dist/src/errors/base.js
CHANGED
|
@@ -106,7 +106,7 @@ export class InternalInvariantError extends FireForgeError {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
|
-
* Raised when a
|
|
109
|
+
* Raised when a regex/brace-depth fallback parser decides it
|
|
110
110
|
* cannot safely perform its mutation, e.g. because a block it expected
|
|
111
111
|
* to walk never closes, because the inserted result fails a round-trip
|
|
112
112
|
* brace balance check, or because an expected pattern is missing.
|
|
@@ -33,7 +33,7 @@ export declare class CorruptRebaseSessionError extends RebaseError {
|
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Thrown by `rebase --dry-run` when the replay rejects at least one patch.
|
|
36
|
-
* A dry run that exited 0 regardless
|
|
36
|
+
* A dry run that exited 0 regardless would read as "the rebase is ready".
|
|
37
37
|
*/
|
|
38
38
|
export declare class RebaseDryRunRejectError extends RebaseError {
|
|
39
39
|
readonly rejected: readonly string[];
|
|
@@ -51,7 +51,7 @@ export class CorruptRebaseSessionError extends RebaseError {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Thrown by `rebase --dry-run` when the replay rejects at least one patch.
|
|
54
|
-
* A dry run that exited 0 regardless
|
|
54
|
+
* A dry run that exited 0 regardless would read as "the rebase is ready".
|
|
55
55
|
*/
|
|
56
56
|
export class RebaseDryRunRejectError extends RebaseError {
|
|
57
57
|
rejected;
|
|
@@ -516,6 +516,8 @@ export interface PatchSplitOptions extends Confirmable, PatchLockWaitable, Unsaf
|
|
|
516
516
|
skipLint?: boolean;
|
|
517
517
|
/** Where a refusal writes its one `--from`-ready declaration report. */
|
|
518
518
|
declarationsOut?: string;
|
|
519
|
+
/** Write the committed old-to-new filename map of renumbered patches here as JSON. */
|
|
520
|
+
mapOut?: string;
|
|
519
521
|
}
|
|
520
522
|
/**
|
|
521
523
|
* Options for the `fireforge patch move-files` subcommand. Without
|
|
@@ -541,6 +543,16 @@ export interface PatchMoveFilesOptions extends Confirmable, PatchLockWaitable, U
|
|
|
541
543
|
skipLint?: boolean;
|
|
542
544
|
/** Where a refusal writes its one `--from`-ready declaration report. */
|
|
543
545
|
declarationsOut?: string;
|
|
546
|
+
/**
|
|
547
|
+
* New engine files no patch owns, adopted into the destination in the
|
|
548
|
+
* same transaction (engine-relative, repeatable).
|
|
549
|
+
*/
|
|
550
|
+
scanFile?: string[];
|
|
551
|
+
/**
|
|
552
|
+
* Allow the move to empty the source: re-point the declarations it owns
|
|
553
|
+
* to the destination and retire its slot. Not valid with `create`.
|
|
554
|
+
*/
|
|
555
|
+
retireEmpty?: boolean;
|
|
544
556
|
}
|
|
545
557
|
/**
|
|
546
558
|
* Options for the rebase command.
|
|
@@ -673,8 +685,8 @@ export interface TestOptions extends EngineLockWaitable {
|
|
|
673
685
|
* build would overwrite engine content recorded in neither a patch body
|
|
674
686
|
* nor the pristine baseline. Same meaning as `fireforge build`'s flag of
|
|
675
687
|
* the same name. A scripted `test --build` is exactly the shape the
|
|
676
|
-
* multi-session hazard shows up in,
|
|
677
|
-
*
|
|
688
|
+
* multi-session hazard shows up in, so it must be able to arm the
|
|
689
|
+
* refusal too. Valid only with `--build`/`--build-only`.
|
|
678
690
|
*/
|
|
679
691
|
refuseUnexportedDrift?: boolean;
|
|
680
692
|
/**
|
|
@@ -1073,7 +1085,7 @@ export interface PatchRenameOptions extends Confirmable, DryRunnable, PatchLockW
|
|
|
1073
1085
|
/**
|
|
1074
1086
|
* New category (validated against configured categories). Rewrites
|
|
1075
1087
|
* the filename prefix and the manifest row in one transaction, so
|
|
1076
|
-
* recategorising
|
|
1088
|
+
* recategorising does not require `patch delete` + fresh export.
|
|
1077
1089
|
*/
|
|
1078
1090
|
category?: string;
|
|
1079
1091
|
/**
|
|
@@ -1096,6 +1108,17 @@ export interface PatchReorderOptions extends PatchLockWaitable {
|
|
|
1096
1108
|
to?: number;
|
|
1097
1109
|
before?: string;
|
|
1098
1110
|
after?: string;
|
|
1111
|
+
/**
|
|
1112
|
+
* Shift a run of patches up by this many orders (`--shift <n> --from
|
|
1113
|
+
* <order> [--through <order>]`) instead of moving one patch.
|
|
1114
|
+
*/
|
|
1115
|
+
shift?: number;
|
|
1116
|
+
/** First order of the run `shift` moves. */
|
|
1117
|
+
from?: number;
|
|
1118
|
+
/** Last order of the run `shift` moves (default: the end of the run). */
|
|
1119
|
+
through?: number;
|
|
1120
|
+
/** Write the committed old-to-new filename map here as JSON. */
|
|
1121
|
+
mapOut?: string;
|
|
1099
1122
|
yes?: boolean;
|
|
1100
1123
|
dryRun?: boolean;
|
|
1101
1124
|
forceUnsafe?: boolean;
|
|
@@ -433,11 +433,10 @@ export interface PatchLintFileSizeTier {
|
|
|
433
433
|
/**
|
|
434
434
|
* Overrides for the `file-too-large` thresholds, per file class.
|
|
435
435
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* lines over 750 has a hard failure with no dial, at exactly the moment
|
|
436
|
+
* Configurable because the recommended gate posture is `--max-warnings 0`:
|
|
437
|
+
* under it the `warning` band is not a soft limit at all, so with fixed
|
|
438
|
+
* thresholds a project whose controllers legitimately sit a few lines over
|
|
439
|
+
* 750 would have a hard failure with no dial, at exactly the moment
|
|
441
440
|
* someone is deciding whether to restructure a file.
|
|
442
441
|
*/
|
|
443
442
|
export interface PatchLintFileSizeThresholds {
|
|
@@ -466,8 +465,7 @@ export interface PatchLintPatchSizeTier {
|
|
|
466
465
|
/**
|
|
467
466
|
* Overrides for the patch-size thresholds, per tier.
|
|
468
467
|
*
|
|
469
|
-
*
|
|
470
|
-
* general 800/1500/3000, test 1500/3000/6000, branding 8000/18000/30000;
|
|
468
|
+
* Defaults: lines general 800/1500/3000, test 1500/3000/6000, branding 8000/18000/30000;
|
|
471
469
|
* files warn above 5 (general, test) or 60 (branding). The files triple has
|
|
472
470
|
* no notice or error band by default: an unset `files.notice` follows the
|
|
473
471
|
* effective `files.warning`, and an unset `files.error` never fires.
|
|
@@ -115,6 +115,13 @@ export interface FurnaceValidationException {
|
|
|
115
115
|
* check for every other element of the component.
|
|
116
116
|
*/
|
|
117
117
|
tag?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Narrows the exception to the findings in this module file of the
|
|
120
|
+
* component (a finding's `files`), by file name: a vendored bundle whose
|
|
121
|
+
* bytes are pinned can be accepted without muting the check for the
|
|
122
|
+
* authored modules beside it.
|
|
123
|
+
*/
|
|
124
|
+
file?: string;
|
|
118
125
|
/** Why the finding is accepted. Required: the entry is the review record. */
|
|
119
126
|
reason: string;
|
|
120
127
|
}
|
|
@@ -379,6 +386,8 @@ export interface ValidationIssue {
|
|
|
379
386
|
message: string;
|
|
380
387
|
/** Element tags the finding is about, when it is about particular elements. */
|
|
381
388
|
tags?: string[];
|
|
389
|
+
/** Component module files the finding is in (`hardcoded-text`), by file name. */
|
|
390
|
+
files?: string[];
|
|
382
391
|
}
|
|
383
392
|
/** Resolved test-harness selection for a `furnace create` run. */
|
|
384
393
|
export type ResolvedTestStyle = 'mochikit' | 'browser-chrome' | 'xpcshell' | 'none';
|
|
@@ -71,8 +71,8 @@ export const WAIT_LOCK_ENV_VAR = 'FIREFORGE_WAIT_LOCK';
|
|
|
71
71
|
/**
|
|
72
72
|
* Bounds shared by the flag and the environment variable. The ceiling is a
|
|
73
73
|
* day: on a shared engine a re-export or `test --build` queued behind a
|
|
74
|
-
* peer's long series can need more than
|
|
75
|
-
* longer wait
|
|
74
|
+
* peer's long series can need more than an hour, and a cap below that turns
|
|
75
|
+
* a longer wait into a requeue after exit 15.
|
|
76
76
|
*/
|
|
77
77
|
const MIN_WAIT_LOCK_SECONDS = 1;
|
|
78
78
|
export const MAX_WAIT_LOCK_SECONDS = 86_400;
|
|
@@ -173,7 +173,7 @@ async function spawnTracked(spec) {
|
|
|
173
173
|
let rejection;
|
|
174
174
|
child.on('error', (error) => {
|
|
175
175
|
// Abort/startup failure: make sure a partially-started tree does not
|
|
176
|
-
// outlive the dispatch (a mach dying at startup
|
|
176
|
+
// outlive the dispatch (a mach dying at startup would otherwise strand
|
|
177
177
|
// multiprocessing workers).
|
|
178
178
|
if (options.processGroup === true) {
|
|
179
179
|
killProcessTree(child, 'SIGKILL', usesProcessGroup);
|
|
@@ -229,7 +229,7 @@ export async function execStream(command, args, options = {}) {
|
|
|
229
229
|
args,
|
|
230
230
|
options,
|
|
231
231
|
graceMs: 1500,
|
|
232
|
-
// A detached group leader
|
|
232
|
+
// A detached group leader does not receive the terminal's Ctrl+C, so
|
|
233
233
|
// the forwarder is mandatory (not just graceful-UX) when processGroup
|
|
234
234
|
// is set. A plain streamed child does not get one.
|
|
235
235
|
forwardWithoutProcessGroup: false,
|
|
@@ -18,7 +18,7 @@ export declare function hasRawCssColors(content: string): boolean;
|
|
|
18
18
|
* the line is blanked, corrupting whatever lint pass consumes the stripped
|
|
19
19
|
* text. The source is tokenized as a module, then as a script. Firefox
|
|
20
20
|
* chrome sources can contain constructs acorn tokenizes under neither goal.
|
|
21
|
-
* Those fall back to the
|
|
21
|
+
* Those fall back to the regex-only strip, which handles strings but not
|
|
22
22
|
* regex literals.
|
|
23
23
|
*/
|
|
24
24
|
export declare function stripJsComments(source: string): string;
|
|
@@ -30,7 +30,7 @@ export declare function stripJsComments(source: string): string;
|
|
|
30
30
|
* legacy octal escape is a tokenizer error only under the module goal.
|
|
31
31
|
* Callers that must not under-count when tokenizing fails (the code-line
|
|
32
32
|
* size metric) read the `null` and pick their own fallback rather than the
|
|
33
|
-
*
|
|
33
|
+
* regex-only strip, which can blank code.
|
|
34
34
|
*/
|
|
35
35
|
export declare function stripJsCommentsParsed(source: string): string | null;
|
|
36
36
|
/**
|
package/dist/src/utils/regex.js
CHANGED
|
@@ -34,7 +34,7 @@ export function hasRawCssColors(content) {
|
|
|
34
34
|
* the line is blanked, corrupting whatever lint pass consumes the stripped
|
|
35
35
|
* text. The source is tokenized as a module, then as a script. Firefox
|
|
36
36
|
* chrome sources can contain constructs acorn tokenizes under neither goal.
|
|
37
|
-
* Those fall back to the
|
|
37
|
+
* Those fall back to the regex-only strip, which handles strings but not
|
|
38
38
|
* regex literals.
|
|
39
39
|
*/
|
|
40
40
|
export function stripJsComments(source) {
|
|
@@ -48,7 +48,7 @@ export function stripJsComments(source) {
|
|
|
48
48
|
* legacy octal escape is a tokenizer error only under the module goal.
|
|
49
49
|
* Callers that must not under-count when tokenizing fails (the code-line
|
|
50
50
|
* size metric) read the `null` and pick their own fallback rather than the
|
|
51
|
-
*
|
|
51
|
+
* regex-only strip, which can blank code.
|
|
52
52
|
*/
|
|
53
53
|
export function stripJsCommentsParsed(source) {
|
|
54
54
|
const comments = collectJsComments(source, 'module') ?? collectJsComments(source, 'script');
|
|
@@ -91,7 +91,7 @@ function collectJsComments(source, sourceType) {
|
|
|
91
91
|
return comments;
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* Pure-regex comment strip, used only when acorn cannot tokenize
|
|
95
95
|
* the source (preprocessor directives, syntax errors). Strings survive.
|
|
96
96
|
* Regex literals are not modeled, so `//` inside a regex blanks the rest
|
|
97
97
|
* of the line.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The single awaitable delay used across FireForge.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Both behaviours now live here, and callers state which one they need.
|
|
4
|
+
* Callers use this rather than a private `sleep`/`delay` or an inline
|
|
5
|
+
* `new Promise((resolve) => setTimeout(resolve, ms))`, because such copies
|
|
6
|
+
* disagree on the one property that actually matters (whether the pending
|
|
7
|
+
* timer holds the event loop open) and the disagreement is invisible at each
|
|
8
|
+
* call site. Both behaviours live here, and callers state which one they need.
|
|
10
9
|
*
|
|
11
10
|
* The default is a ref'd timer, because the dangerous mistake is the other
|
|
12
11
|
* one: an unref'd delay awaited between a SIGTERM and its escalation lets Node
|
package/dist/src/utils/sleep.js
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* The single awaitable delay used across FireForge.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Both behaviours now live here, and callers state which one they need.
|
|
5
|
+
* Callers use this rather than a private `sleep`/`delay` or an inline
|
|
6
|
+
* `new Promise((resolve) => setTimeout(resolve, ms))`, because such copies
|
|
7
|
+
* disagree on the one property that actually matters (whether the pending
|
|
8
|
+
* timer holds the event loop open) and the disagreement is invisible at each
|
|
9
|
+
* call site. Both behaviours live here, and callers state which one they need.
|
|
11
10
|
*
|
|
12
11
|
* The default is a ref'd timer, because the dangerous mistake is the other
|
|
13
12
|
* one: an unref'd delay awaited between a SIGTERM and its escalation lets Node
|
|
@@ -20,8 +20,8 @@ export function isString(value) {
|
|
|
20
20
|
*/
|
|
21
21
|
export function isNumber(value) {
|
|
22
22
|
// Number.isFinite matches the documented contract: NaN and ±Infinity are
|
|
23
|
-
// excluded (a JSON config field of 1e999 parses to Infinity
|
|
24
|
-
// validate as a legal number).
|
|
23
|
+
// excluded (a JSON config field of 1e999 parses to Infinity, which must
|
|
24
|
+
// not validate as a legal number).
|
|
25
25
|
return typeof value === 'number' && Number.isFinite(value);
|
|
26
26
|
}
|
|
27
27
|
/**
|