@c9up/helix 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +274 -5
  2. package/bin/helix.js +312 -13
  3. package/dist/cli/failed-cache.d.ts +27 -0
  4. package/dist/cli/failed-cache.d.ts.map +1 -0
  5. package/dist/cli/failed-cache.js +64 -0
  6. package/dist/cli/failed-cache.js.map +1 -0
  7. package/dist/cli/glob.d.ts +36 -0
  8. package/dist/cli/glob.d.ts.map +1 -0
  9. package/dist/cli/glob.js +193 -0
  10. package/dist/cli/glob.js.map +1 -0
  11. package/dist/cli/native.d.ts +4 -0
  12. package/dist/cli/native.d.ts.map +1 -1
  13. package/dist/cli/native.js.map +1 -1
  14. package/dist/cli/pool.d.ts +11 -0
  15. package/dist/cli/pool.d.ts.map +1 -1
  16. package/dist/cli/pool.js +13 -7
  17. package/dist/cli/pool.js.map +1 -1
  18. package/dist/cli/reporter.d.ts +11 -0
  19. package/dist/cli/reporter.d.ts.map +1 -1
  20. package/dist/cli/reporter.js +53 -0
  21. package/dist/cli/reporter.js.map +1 -1
  22. package/dist/cli/run.d.ts +41 -0
  23. package/dist/cli/run.d.ts.map +1 -1
  24. package/dist/cli/run.js +181 -21
  25. package/dist/cli/run.js.map +1 -1
  26. package/dist/cli/runner.d.ts +19 -0
  27. package/dist/cli/runner.d.ts.map +1 -0
  28. package/dist/cli/runner.js +15 -0
  29. package/dist/cli/runner.js.map +1 -0
  30. package/dist/cli/suites.d.ts +102 -0
  31. package/dist/cli/suites.d.ts.map +1 -0
  32. package/dist/cli/suites.js +226 -0
  33. package/dist/cli/suites.js.map +1 -0
  34. package/dist/index.d.ts +10 -9
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +9 -8
  37. package/dist/index.js.map +1 -1
  38. package/dist/japa/core.d.ts +106 -0
  39. package/dist/japa/core.d.ts.map +1 -0
  40. package/dist/japa/core.js +128 -0
  41. package/dist/japa/core.js.map +1 -0
  42. package/dist/runtime/assert.d.ts +174 -0
  43. package/dist/runtime/assert.d.ts.map +1 -0
  44. package/dist/runtime/assert.js +591 -0
  45. package/dist/runtime/assert.js.map +1 -0
  46. package/dist/runtime/asymmetric-brand.d.ts +20 -0
  47. package/dist/runtime/asymmetric-brand.d.ts.map +1 -0
  48. package/dist/runtime/asymmetric-brand.js +18 -0
  49. package/dist/runtime/asymmetric-brand.js.map +1 -0
  50. package/dist/runtime/asymmetric.d.ts +2 -10
  51. package/dist/runtime/asymmetric.d.ts.map +1 -1
  52. package/dist/runtime/asymmetric.js +3 -7
  53. package/dist/runtime/asymmetric.js.map +1 -1
  54. package/dist/runtime/bootstrap.d.ts +47 -0
  55. package/dist/runtime/bootstrap.d.ts.map +1 -0
  56. package/dist/runtime/bootstrap.js +166 -0
  57. package/dist/runtime/bootstrap.js.map +1 -0
  58. package/dist/runtime/cli-args.d.ts +77 -0
  59. package/dist/runtime/cli-args.d.ts.map +1 -0
  60. package/dist/runtime/cli-args.js +85 -0
  61. package/dist/runtime/cli-args.js.map +1 -0
  62. package/dist/runtime/cli-worker.js +1 -2
  63. package/dist/runtime/cli-worker.js.map +1 -1
  64. package/dist/runtime/configure.d.ts +214 -0
  65. package/dist/runtime/configure.d.ts.map +1 -0
  66. package/dist/runtime/configure.js +164 -0
  67. package/dist/runtime/configure.js.map +1 -0
  68. package/dist/runtime/context.d.ts +84 -0
  69. package/dist/runtime/context.d.ts.map +1 -0
  70. package/dist/runtime/context.js +146 -0
  71. package/dist/runtime/context.js.map +1 -0
  72. package/dist/runtime/emitter.d.ts +141 -0
  73. package/dist/runtime/emitter.d.ts.map +1 -0
  74. package/dist/runtime/emitter.js +79 -0
  75. package/dist/runtime/emitter.js.map +1 -0
  76. package/dist/runtime/equals.d.ts.map +1 -1
  77. package/dist/runtime/equals.js +6 -10
  78. package/dist/runtime/equals.js.map +1 -1
  79. package/dist/runtime/global-hooks.d.ts +31 -0
  80. package/dist/runtime/global-hooks.d.ts.map +1 -0
  81. package/dist/runtime/global-hooks.js +126 -0
  82. package/dist/runtime/global-hooks.js.map +1 -0
  83. package/dist/runtime/index.d.ts +14 -3
  84. package/dist/runtime/index.d.ts.map +1 -1
  85. package/dist/runtime/index.js +7 -2
  86. package/dist/runtime/index.js.map +1 -1
  87. package/dist/runtime/ipc-protocol.d.ts +21 -0
  88. package/dist/runtime/ipc-protocol.d.ts.map +1 -0
  89. package/dist/runtime/ipc-protocol.js +21 -0
  90. package/dist/runtime/ipc-protocol.js.map +1 -0
  91. package/dist/runtime/run.d.ts +49 -2
  92. package/dist/runtime/run.d.ts.map +1 -1
  93. package/dist/runtime/run.js +618 -94
  94. package/dist/runtime/run.js.map +1 -1
  95. package/dist/runtime/runner.d.ts +97 -0
  96. package/dist/runtime/runner.d.ts.map +1 -0
  97. package/dist/runtime/runner.js +160 -0
  98. package/dist/runtime/runner.js.map +1 -0
  99. package/dist/runtime/suite-config.d.ts +23 -0
  100. package/dist/runtime/suite-config.d.ts.map +1 -0
  101. package/dist/runtime/suite-config.js +52 -0
  102. package/dist/runtime/suite-config.js.map +1 -0
  103. package/dist/runtime/suite-taps.d.ts +122 -0
  104. package/dist/runtime/suite-taps.d.ts.map +1 -0
  105. package/dist/runtime/suite-taps.js +218 -0
  106. package/dist/runtime/suite-taps.js.map +1 -0
  107. package/dist/runtime/suite.d.ts +277 -8
  108. package/dist/runtime/suite.d.ts.map +1 -1
  109. package/dist/runtime/suite.js +255 -4
  110. package/dist/runtime/suite.js.map +1 -1
  111. package/dist/runtime/test-context.d.ts +50 -3
  112. package/dist/runtime/test-context.d.ts.map +1 -1
  113. package/dist/runtime/test-context.js +83 -5
  114. package/dist/runtime/test-context.js.map +1 -1
  115. package/dist/runtime/worker.d.ts.map +1 -1
  116. package/dist/runtime/worker.js +86 -29
  117. package/dist/runtime/worker.js.map +1 -1
  118. package/index.darwin-arm64.node +0 -0
  119. package/index.darwin-x64.node +0 -0
  120. package/index.linux-arm64-gnu.node +0 -0
  121. package/index.linux-x64-gnu.node +0 -0
  122. package/index.win32-x64-msvc.node +0 -0
  123. package/package.json +8 -1
@@ -0,0 +1,214 @@
1
+ /**
2
+ * `configure({ plugins })` — the Japa/AdonisJS bootstrap entry.
3
+ *
4
+ * Following Japa precisely: the runtime core is plugin-agnostic, and every
5
+ * capability (HTTP `client`, `db`, fakes, …) ships as a PLUGIN that extends the
6
+ * injected test context. A plugin is a function handed a {@link PluginApi};
7
+ * it registers context properties via `api.context.macro` / `.getter` (the
8
+ * `TestContext.macro`/`getter` of Japa) and pairs that with a `declare module`
9
+ * augmentation for the types.
10
+ *
11
+ * // tests/bootstrap.ts
12
+ * import { configure } from "@c9up/helix";
13
+ * import { apiClient } from "@c9up/ream/testing";
14
+ * await configure({ plugins: [apiClient({ baseUrl })] });
15
+ *
16
+ * This keeps `@c9up/helix` (the core) free of any ecosystem dependency: the
17
+ * plugins live in each package's `/testing` subpath and depend on helix, never
18
+ * the other way round — the Japa "runner + plugins" topology.
19
+ */
20
+ import { type CLIArgs } from "./cli-args.js";
21
+ import { type TestContext } from "./context.js";
22
+ import { type Emitter } from "./emitter.js";
23
+ import { Runner } from "./runner.js";
24
+ import { type SuiteHandle, type SuiteHook, type SuiteHookCleanup } from "./suite-taps.js";
25
+ /**
26
+ * What a plugin uses to extend the test context. Mirrors Japa's
27
+ * `TestContext.macro(name, value)` / `TestContext.getter(name, fn)`.
28
+ */
29
+ export interface PluginContext {
30
+ /** Add a shared property present on every test context. */
31
+ macro(name: string, value: unknown): void;
32
+ /** Add a lazily-computed, per-context property (cached per context). */
33
+ getter(name: string, fn: (ctx: TestContext) => unknown): void;
34
+ }
35
+ /** A runner-level hook — Japa's shape, defined once in `suite-taps.ts`. */
36
+ export type RunnerHook = SuiteHook;
37
+ export type RunnerHookCleanup = SuiteHookCleanup;
38
+ /**
39
+ * The API handed to each plugin at {@link configure} time.
40
+ *
41
+ * Japa hands its plugins `{ config, cliArgs, runner, emitter }`; helix passes
42
+ * the same four, so a Japa plugin's body ports over unchanged, plus two helix
43
+ * additions — `context` (Japa reaches the same registry through the imported
44
+ * `TestContext` class) and `cleanup` (Japa uses `config.teardown`).
45
+ */
46
+ export interface PluginApi {
47
+ /**
48
+ * The options this run was configured with (Japa `config`). Mutable: Japa
49
+ * plugins edit it, and helix reads it back once every plugin has run.
50
+ */
51
+ config: ConfigureOptions;
52
+ /** The flags the CLI forwarded to this worker (Japa `cliArgs`). */
53
+ cliArgs: CLIArgs;
54
+ /** Run-level counters, readable once the run ends (Japa `runner`). */
55
+ runner: Runner;
56
+ /** Lifecycle events — `test:start`, `group:end`, … (Japa `emitter`). */
57
+ emitter: Emitter;
58
+ /** Extend the injected test context. */
59
+ context: PluginContext;
60
+ /**
61
+ * Register a teardown that runs ONCE after all tests in the run finish
62
+ * (reverse registration order) — the place to close a booted server, a DB
63
+ * pool, etc. (Japa runner-teardown parity). Without this, a plugin that boots
64
+ * a resource at `configure()` has no clean shutdown point.
65
+ */
66
+ cleanup(fn: RunnerHook): void;
67
+ }
68
+ /**
69
+ * A helix plugin — the Japa plugin shape adapted to helix. Runs once at
70
+ * bootstrap; may be async (e.g. to boot a server before registering `client`).
71
+ */
72
+ export type Plugin = (api: PluginApi) => void | Promise<void>;
73
+ /**
74
+ * Filters applied to the tests a file declares (Japa `config.filters`).
75
+ *
76
+ * `files` and `suites` are reported, not honoured: they select which FILES run,
77
+ * and helix settles that list in the CLI process before any worker — and
78
+ * therefore any bootstrap — exists. Setting them here cannot un-spawn a worker
79
+ * that is already running, so they carry what the CLI decided and the CLI-side
80
+ * flags (`--files`, a suite positional) remain the way to decide it.
81
+ */
82
+ export interface ConfigureFilters {
83
+ /**
84
+ * Path fragments the CLI matched files against (Japa `filters.files`).
85
+ * Read-only here: helix settles the file list before a worker exists, so
86
+ * this reports what was selected rather than selecting.
87
+ */
88
+ files?: string[];
89
+ /** Suite names the run was limited to (Japa `filters.suites`). Read-only. */
90
+ suites?: string[];
91
+ /** Only tests carrying one of these tags (`~@tag`/`!@tag` excludes). */
92
+ tags?: string[];
93
+ /** Only groups with these exact titles. */
94
+ groups?: string[];
95
+ /** Only tests with these exact titles. */
96
+ tests?: string[];
97
+ /** Require EVERY tag in `tags` instead of any (Japa `--match-all`). */
98
+ matchAll?: boolean;
99
+ }
100
+ /**
101
+ * Japa's `Refiner`, as much of it as means anything here: a handle that ADDS
102
+ * filters. It writes straight into {@link ConfigureOptions.filters}, so a
103
+ * plugin calling `refiner.add("tags", [...])` steers the run exactly as setting
104
+ * the filter would — the object is a different door to the same room, not a
105
+ * second mechanism.
106
+ */
107
+ export interface Refiner {
108
+ /** Add filter values for a layer (Japa `refiner.add`). */
109
+ add(layer: "tests" | "groups" | "tags", values: string[]): void;
110
+ /** Require every tag instead of any (Japa `refiner.matchAllTags`). */
111
+ matchAllTags(toggle?: boolean): void;
112
+ }
113
+ /** Runtime configuration passed to {@link configure}. */
114
+ export interface ConfigureOptions {
115
+ /** Plugins to install — each extends the test context (Japa parity). */
116
+ plugins?: Plugin[];
117
+ /**
118
+ * The directory the run was launched from (Japa `cwd`). Filled in by the
119
+ * runtime, so a plugin resolving a path against the project reads the same
120
+ * root the CLI discovered from.
121
+ */
122
+ cwd?: string;
123
+ /**
124
+ * Configure the suite before it runs (Japa `configureSuite`). Applied AFTER
125
+ * the plugins, which is both Japa's order and what lets a plugin read it or
126
+ * put its own in place.
127
+ *
128
+ * May return a promise — Japa's is synchronous, but helix's per-suite
129
+ * `configure` has to re-import the config module, and hooks it registers must
130
+ * exist before the setup hooks are drained.
131
+ */
132
+ configureSuite?: (suite: SuiteHandle) => void | Promise<void>;
133
+ /**
134
+ * The reporters this run activated (Japa `reporters.activated`). Read-only
135
+ * truth: reporters live in the CLI process, so naming one here would not
136
+ * make it run.
137
+ */
138
+ reporters?: {
139
+ activated: string[];
140
+ };
141
+ /** `process.exit()` once the run ends (Japa `forceExit`). */
142
+ forceExit?: boolean;
143
+ /** Directories discovery skipped (Japa `exclude`). */
144
+ exclude?: string[];
145
+ /**
146
+ * The filter object (Japa `refiner`). Writes through to
147
+ * {@link ConfigureOptions.filters}, so `refiner.add("tags", [...])` from a
148
+ * plugin steers the run exactly as setting the filter would.
149
+ */
150
+ refiner?: Refiner;
151
+ /**
152
+ * Filters to apply to this file's tests (Japa `config.filters`). The CLI
153
+ * flags win: a filter typed at the prompt overrides the configured one.
154
+ */
155
+ filters?: ConfigureFilters;
156
+ /**
157
+ * How a test file is imported (Japa `config.importer`). Defaults to
158
+ * `import(file.href)`. Receives the URL helix would have imported —
159
+ * cache-busting query included, so repeated runs still re-evaluate.
160
+ */
161
+ importer?: (file: URL) => void | Promise<void>;
162
+ /** Run once before the tests (Japa runner `setup`). */
163
+ setup?: RunnerHook[];
164
+ /** Run once after the tests, reverse order (Japa runner `teardown`). */
165
+ teardown?: RunnerHook[];
166
+ /**
167
+ * Default per-test timeout in ms for this file's tests (Japa `configure({
168
+ * timeout })`). `0` disables. Overridden by `--timeout` and by a per-test
169
+ * `test.timeout(ms)` / `{ timeout }`.
170
+ */
171
+ timeout?: number;
172
+ /**
173
+ * Default extra attempts on failure for this file's tests (Japa `configure({
174
+ * retries })`). Overridden by `--retries` and by a per-test `test.retry(n)`.
175
+ */
176
+ retries?: number;
177
+ /**
178
+ * The name of the suite these tests belong to — surfaced as
179
+ * `ctx.test.options.meta.suite.name` and on the `suite:*` events. Defaults to
180
+ * `"default"`, the name Japa gives its implicit suite. Overridden by
181
+ * `--suite`.
182
+ */
183
+ suite?: string;
184
+ }
185
+ /** Run-level defaults from `configure({ timeout, retries, suite, filters })`. */
186
+ interface ConfiguredDefaults {
187
+ timeout?: number;
188
+ retries?: number;
189
+ suite?: string;
190
+ filters?: ConfigureFilters;
191
+ importer?: (file: URL) => void | Promise<void>;
192
+ }
193
+ /** The defaults set by {@link configure}, if any. */
194
+ export declare function getConfiguredDefaults(): Readonly<ConfiguredDefaults>;
195
+ /**
196
+ * Install plugins + runner hooks. Call once from a bootstrap file
197
+ * (Japa/AdonisJS `bin/test.ts` / `tests/bootstrap.ts`).
198
+ *
199
+ * Order follows Japa: PLUGINS first, then the run's `setup` hooks, then the
200
+ * `teardown` hooks are parked for after the run (see {@link
201
+ * drainRunnerTeardowns}). That ordering is what makes a plugin's edits count —
202
+ * it can raise `config.timeout`, push a `setup` hook, or reach for
203
+ * `runner.onSuite`, and the run picks all of it up because nothing has been
204
+ * read yet.
205
+ */
206
+ export declare function configure(options: ConfigureOptions): Promise<void>;
207
+ /**
208
+ * Run every registered runner teardown (reverse order), then clear them — called
209
+ * by the runtime after a file's tests finish. Failures are logged, not thrown,
210
+ * so one bad teardown can't hide the test results.
211
+ */
212
+ export declare function drainRunnerTeardowns(): Promise<void>;
213
+ export {};
214
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../src/runtime/configure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * `configure({ plugins })` — the Japa/AdonisJS bootstrap entry.
3
+ *
4
+ * Following Japa precisely: the runtime core is plugin-agnostic, and every
5
+ * capability (HTTP `client`, `db`, fakes, …) ships as a PLUGIN that extends the
6
+ * injected test context. A plugin is a function handed a {@link PluginApi};
7
+ * it registers context properties via `api.context.macro` / `.getter` (the
8
+ * `TestContext.macro`/`getter` of Japa) and pairs that with a `declare module`
9
+ * augmentation for the types.
10
+ *
11
+ * // tests/bootstrap.ts
12
+ * import { configure } from "@c9up/helix";
13
+ * import { apiClient } from "@c9up/ream/testing";
14
+ * await configure({ plugins: [apiClient({ baseUrl })] });
15
+ *
16
+ * This keeps `@c9up/helix` (the core) free of any ecosystem dependency: the
17
+ * plugins live in each package's `/testing` subpath and depend on helix, never
18
+ * the other way round — the Japa "runner + plugins" topology.
19
+ */
20
+ import { cliArgs } from "./cli-args.js";
21
+ import { TestContextRegistry } from "./context.js";
22
+ import { emitter } from "./emitter.js";
23
+ import { Runner } from "./runner.js";
24
+ import { makeSuiteHandle, setCurrentSuite, } from "./suite-taps.js";
25
+ /** Build a {@link Refiner} writing into `filters`. */
26
+ function makeRefiner(filters) {
27
+ return {
28
+ add(layer, values) {
29
+ filters[layer] = [...(filters[layer] ?? []), ...values];
30
+ },
31
+ matchAllTags(toggle = true) {
32
+ filters.matchAll = toggle;
33
+ },
34
+ };
35
+ }
36
+ /** Teardowns to run after the run — from `configure({ teardown })` + `api.cleanup`. */
37
+ const runnerTeardowns = [];
38
+ /** Undos returned by `setup` hooks — drained BEFORE the teardowns. */
39
+ const runnerCleanups = [];
40
+ const configuredDefaults = {};
41
+ /** The defaults set by {@link configure}, if any. */
42
+ export function getConfiguredDefaults() {
43
+ return configuredDefaults;
44
+ }
45
+ /** Tracks the run by listening to {@link emitter} — handed to plugins. */
46
+ const runner = new Runner(emitter);
47
+ /** The options the last {@link configure} call resolved to (Japa `config`). */
48
+ let resolvedConfig = {};
49
+ const api = {
50
+ // A getter so a plugin reads the config of the `configure()` call it is
51
+ // running under, not an empty object captured at module load. Japa lets a
52
+ // plugin EDIT it (that is how a plugin raises the default timeout), so the
53
+ // object is handed over mutable and read back after every plugin has run.
54
+ get config() {
55
+ return resolvedConfig;
56
+ },
57
+ // The same object every access, for the same reason: a plugin that writes
58
+ // `api.cliArgs.tags = [...]` must actually steer the run.
59
+ get cliArgs() {
60
+ return cliArgs();
61
+ },
62
+ runner,
63
+ emitter,
64
+ context: {
65
+ macro: (name, value) => TestContextRegistry.macro(name, value),
66
+ getter: (name, fn) => TestContextRegistry.getter(name, fn),
67
+ },
68
+ cleanup: (fn) => {
69
+ runnerTeardowns.push(fn);
70
+ },
71
+ };
72
+ /**
73
+ * Install plugins + runner hooks. Call once from a bootstrap file
74
+ * (Japa/AdonisJS `bin/test.ts` / `tests/bootstrap.ts`).
75
+ *
76
+ * Order follows Japa: PLUGINS first, then the run's `setup` hooks, then the
77
+ * `teardown` hooks are parked for after the run (see {@link
78
+ * drainRunnerTeardowns}). That ordering is what makes a plugin's edits count —
79
+ * it can raise `config.timeout`, push a `setup` hook, or reach for
80
+ * `runner.onSuite`, and the run picks all of it up because nothing has been
81
+ * read yet.
82
+ */
83
+ export async function configure(options) {
84
+ resolvedConfig = options;
85
+ // Hook arrays a plugin can append to (through `runner.onSuite`) — they must
86
+ // exist before the plugins run, and they are the very arrays drained below.
87
+ resolvedConfig.setup ??= [];
88
+ resolvedConfig.teardown ??= [];
89
+ const setup = resolvedConfig.setup;
90
+ const teardown = resolvedConfig.teardown;
91
+ const handle = makeSuiteHandle(options.suite ?? "default", setup, teardown);
92
+ setCurrentSuite(handle);
93
+ // The rest of Japa's `BaseConfig`, filled with what this run actually is, so
94
+ // a plugin reading `api.config` is told the truth rather than `undefined`.
95
+ const flags = cliArgs();
96
+ resolvedConfig.cwd ??= process.cwd();
97
+ resolvedConfig.reporters ??= { activated: flags.reporters ?? [] };
98
+ resolvedConfig.forceExit ??= flags.forceExit === true;
99
+ resolvedConfig.filters ??= {};
100
+ resolvedConfig.filters.files ??= flags.files;
101
+ resolvedConfig.filters.suites ??=
102
+ flags.suite === undefined ? undefined : [flags.suite];
103
+ resolvedConfig.refiner ??= makeRefiner(resolvedConfig.filters);
104
+ for (const plugin of resolvedConfig.plugins ?? []) {
105
+ await plugin(api);
106
+ }
107
+ // Japa order: plugins, THEN `runner.onSuite(config.configureSuite)`, then the
108
+ // setup hooks. Applying it here rather than earlier is what lets a plugin
109
+ // read it — or put its own in place — and still have it take effect.
110
+ await resolvedConfig.configureSuite?.(handle);
111
+ // Read the defaults back AFTER the plugins, so a plugin that edited the
112
+ // config steers the run rather than writing into a value already consumed.
113
+ if (resolvedConfig.timeout !== undefined)
114
+ configuredDefaults.timeout = resolvedConfig.timeout;
115
+ if (resolvedConfig.retries !== undefined)
116
+ configuredDefaults.retries = resolvedConfig.retries;
117
+ if (resolvedConfig.suite !== undefined)
118
+ configuredDefaults.suite = resolvedConfig.suite;
119
+ if (resolvedConfig.filters !== undefined)
120
+ configuredDefaults.filters = resolvedConfig.filters;
121
+ if (resolvedConfig.importer !== undefined)
122
+ configuredDefaults.importer = resolvedConfig.importer;
123
+ // Japa skips the global setup hooks under `--list-pinned`: nothing runs, so
124
+ // nothing should be opened.
125
+ const listing = cliArgs().listPinned === true;
126
+ for (const fn of listing ? [] : setup) {
127
+ // A `setup` hook may resolve to its own undo (the AdonisJS idiom); park it
128
+ // with the teardowns so it runs in reverse order with everything else.
129
+ const undo = await fn(runner);
130
+ if (typeof undo === "function")
131
+ runnerCleanups.push(undo);
132
+ }
133
+ for (const fn of teardown)
134
+ runnerTeardowns.push(fn);
135
+ }
136
+ /**
137
+ * Run every registered runner teardown (reverse order), then clear them — called
138
+ * by the runtime after a file's tests finish. Failures are logged, not thrown,
139
+ * so one bad teardown can't hide the test results.
140
+ */
141
+ export async function drainRunnerTeardowns() {
142
+ // Cleanups returned by `setup` unwind first — they are the innermost thing
143
+ // that was opened. `null` for the error: the drain only happens once the run
144
+ // itself has finished, so there is no setup failure left to report.
145
+ for (let i = runnerCleanups.length - 1; i >= 0; i -= 1) {
146
+ try {
147
+ await runnerCleanups[i](null, runner);
148
+ }
149
+ catch (err) {
150
+ console.error("[helix] runner cleanup failed:", err);
151
+ }
152
+ }
153
+ runnerCleanups.length = 0;
154
+ for (let i = runnerTeardowns.length - 1; i >= 0; i -= 1) {
155
+ try {
156
+ await runnerTeardowns[i](runner);
157
+ }
158
+ catch (err) {
159
+ console.error("[helix] runner teardown failed:", err);
160
+ }
161
+ }
162
+ runnerTeardowns.length = 0;
163
+ }
164
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../../src/runtime/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAgB,OAAO,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAoB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAgB,OAAO,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACN,eAAe,EAIf,eAAe,GACf,MAAM,iBAAiB,CAAC;AAgGzB,sDAAsD;AACtD,SAAS,WAAW,CAAC,OAAyB;IAC7C,OAAO;QACN,GAAG,CAAC,KAAK,EAAE,MAAM;YAChB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;QACzD,CAAC;QACD,YAAY,CAAC,MAAM,GAAG,IAAI;YACzB,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC;QAC3B,CAAC;KACD,CAAC;AACH,CAAC;AAyED,uFAAuF;AACvF,MAAM,eAAe,GAAiB,EAAE,CAAC;AAEzC,sEAAsE;AACtE,MAAM,cAAc,GAAwB,EAAE,CAAC;AAW/C,MAAM,kBAAkB,GAAuB,EAAE,CAAC;AAElD,qDAAqD;AACrD,MAAM,UAAU,qBAAqB;IACpC,OAAO,kBAAkB,CAAC;AAC3B,CAAC;AAED,0EAA0E;AAC1E,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnC,+EAA+E;AAC/E,IAAI,cAAc,GAAqB,EAAE,CAAC;AAE1C,MAAM,GAAG,GAAc;IACtB,wEAAwE;IACxE,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,IAAI,MAAM;QACT,OAAO,cAAc,CAAC;IACvB,CAAC;IACD,0EAA0E;IAC1E,0DAA0D;IAC1D,IAAI,OAAO;QACV,OAAO,OAAO,EAAE,CAAC;IAClB,CAAC;IACD,MAAM;IACN,OAAO;IACP,OAAO,EAAE;QACR,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;QAC9D,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;KAC1D;IACD,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE;QACf,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;CACD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB;IACxD,cAAc,GAAG,OAAO,CAAC;IACzB,4EAA4E;IAC5E,4EAA4E;IAC5E,cAAc,CAAC,KAAK,KAAK,EAAE,CAAC;IAC5B,cAAc,CAAC,QAAQ,KAAK,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC;IACnC,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC;IACzC,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC5E,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,6EAA6E;IAC7E,2EAA2E;IAC3E,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,cAAc,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;IACrC,cAAc,CAAC,SAAS,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;IAClE,cAAc,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC;IACtD,cAAc,CAAC,OAAO,KAAK,EAAE,CAAC;IAC9B,cAAc,CAAC,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;IAC7C,cAAc,CAAC,OAAO,CAAC,MAAM;QAC5B,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvD,cAAc,CAAC,OAAO,KAAK,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAE/D,KAAK,MAAM,MAAM,IAAI,cAAc,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,cAAc,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC;IAE9C,wEAAwE;IACxE,2EAA2E;IAC3E,IAAI,cAAc,CAAC,OAAO,KAAK,SAAS;QACvC,kBAAkB,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC;IACrD,IAAI,cAAc,CAAC,OAAO,KAAK,SAAS;QACvC,kBAAkB,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC;IACrD,IAAI,cAAc,CAAC,KAAK,KAAK,SAAS;QACrC,kBAAkB,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC;IACjD,IAAI,cAAc,CAAC,OAAO,KAAK,SAAS;QACvC,kBAAkB,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC;IACrD,IAAI,cAAc,CAAC,QAAQ,KAAK,SAAS;QACxC,kBAAkB,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC;IAEvD,4EAA4E;IAC5E,4BAA4B;IAC5B,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC,UAAU,KAAK,IAAI,CAAC;IAC9C,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACvC,2EAA2E;QAC3E,uEAAuE;QACvE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,OAAO,IAAI,KAAK,UAAU;YAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,QAAQ;QAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACzC,2EAA2E;IAC3E,6EAA6E;IAC7E,oEAAoE;IACpE,KAAK,IAAI,CAAC,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC;YACJ,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;IACF,CAAC;IACD,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC;YACJ,MAAM,eAAe,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;IACF,CAAC;IACD,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Injected test context — Japa's defining mechanism.
3
+ *
4
+ * Every test body (and, later, group hooks) receives a `TestContext` as its
5
+ * FIRST argument: `test("x", ({ assert, client, db, cleanup }) => …)`. The
6
+ * context is EXTENSIBLE via declaration merging: capability plugins (HTTP
7
+ * client, DB, fakes) both register a runtime property through
8
+ * {@link TestContextRegistry} AND augment the `TestContext` interface:
9
+ *
10
+ * declare module "@c9up/helix" {
11
+ * interface TestContext { client: ApiClient }
12
+ * }
13
+ *
14
+ * This is what lets `@c9up/helix/http`, `@c9up/helix/db`, … graft `client`,
15
+ * `db`, … onto the context — fully typed — without the core depending on those
16
+ * packages.
17
+ *
18
+ * Backwards compatible: passing the context as the first arg is a no-op for
19
+ * existing zero-argument bodies (`test("x", () => …)`), which ignore it.
20
+ */
21
+ import { type Assert } from "./assert.js";
22
+ import type { TestInstance } from "./suite.js";
23
+ import { type TestCleanup } from "./test-context.js";
24
+ /**
25
+ * The per-test context. `cleanup` is always present; capability plugins add
26
+ * `assert`, `client`, `db`, … by augmenting this interface (declaration
27
+ * merging) alongside a matching {@link TestContextRegistry} registration.
28
+ */
29
+ /**
30
+ * A CLASS (not an interface) so plugins can add REQUIRED properties via
31
+ * declaration merging — `declare module "@c9up/helix" { interface TestContext {
32
+ * client: ApiClient } }` — without breaking the core's construction (an external
33
+ * augmentation is not subject to helix's `strictPropertyInitialization`). This
34
+ * is exactly how Japa's `TestContext` works.
35
+ */
36
+ export declare class TestContext {
37
+ /**
38
+ * Add a shared property to every test context — Japa's
39
+ * `TestContext.macro('sleep', fn)`, callable on the class itself so a Japa
40
+ * plugin's registration code ports over unchanged.
41
+ */
42
+ static macro(name: string, value: unknown): void;
43
+ /** Add a lazily-computed, per-context property (Japa `TestContext.getter`). */
44
+ static getter(name: string, fn: Getter): void;
45
+ /** Chai-flavored assertions (`@japa/assert` parity), alongside `expect`. */
46
+ readonly assert: Assert;
47
+ /** The running test's own instance — name, options, dataset (Japa `ctx.test`). */
48
+ readonly test: TestInstance;
49
+ constructor(test: TestInstance);
50
+ /**
51
+ * Register a teardown that runs at the end of THIS test, regardless of
52
+ * outcome (Japa `ctx.cleanup`). Reverse-insertion order, isolated failures.
53
+ */
54
+ cleanup(fn: TestCleanup): void;
55
+ }
56
+ export type Getter = (this: TestContext, ctx: TestContext) => unknown;
57
+ /**
58
+ * Registry through which plugins extend the test context at runtime. Mirrors
59
+ * Japa's `TestContext.macro(name, value)` / `TestContext.getter(name, fn)`.
60
+ * Pair each call with a `declare module` augmentation for the types.
61
+ */
62
+ export declare const TestContextRegistry: {
63
+ /** Add a shared property present on every test context. */
64
+ macro(name: string, value: unknown): void;
65
+ /**
66
+ * Add a lazily-computed, per-context property. The getter runs the first
67
+ * time the property is read on a given context (inside that test's frame),
68
+ * and the result is cached for that context.
69
+ */
70
+ getter(name: string, fn: Getter): void;
71
+ /** Remove a macro/getter (plugin teardown, tests). No arg clears all. */
72
+ clear(name?: string): void;
73
+ /** Whether any extension is registered under `name`. */
74
+ has(name: string): boolean;
75
+ };
76
+ /**
77
+ * Build a fresh `TestContext` for one test attempt. MUST be called inside the
78
+ * per-test frame (`withTestContext`) so `ctx.cleanup` reaches the right frame.
79
+ * Applies registered macros (own value), then getters (lazy, cached per
80
+ * context). Dynamic props are attached via `defineProperty` so no `cleanup`
81
+ * override and no cast are needed.
82
+ */
83
+ export declare function buildTestContext(test: TestInstance): TestContext;
84
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE1E;;;;GAIG;AACH;;;;;;GAMG;AACH,qBAAa,WAAW;IACvB;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAIhD,+EAA+E;IAC/E,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAI7C,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;gBAEhB,IAAI,EAAE,YAAY;IAK9B;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;CAK9B;AAID,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC;AAKtE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;IAC/B,2DAA2D;gBAC/C,MAAM,SAAS,OAAO,GAAG,IAAI;IAGzC;;;;OAIG;iBACU,MAAM,MAAM,MAAM,GAAG,IAAI;IAGtC,yEAAyE;iBAC5D,MAAM,GAAG,IAAI;IAS1B,wDAAwD;cAC9C,MAAM,GAAG,OAAO;CAG1B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAqChE"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Injected test context — Japa's defining mechanism.
3
+ *
4
+ * Every test body (and, later, group hooks) receives a `TestContext` as its
5
+ * FIRST argument: `test("x", ({ assert, client, db, cleanup }) => …)`. The
6
+ * context is EXTENSIBLE via declaration merging: capability plugins (HTTP
7
+ * client, DB, fakes) both register a runtime property through
8
+ * {@link TestContextRegistry} AND augment the `TestContext` interface:
9
+ *
10
+ * declare module "@c9up/helix" {
11
+ * interface TestContext { client: ApiClient }
12
+ * }
13
+ *
14
+ * This is what lets `@c9up/helix/http`, `@c9up/helix/db`, … graft `client`,
15
+ * `db`, … onto the context — fully typed — without the core depending on those
16
+ * packages.
17
+ *
18
+ * Backwards compatible: passing the context as the first arg is a no-op for
19
+ * existing zero-argument bodies (`test("x", () => …)`), which ignore it.
20
+ */
21
+ import { createAssert } from "./assert.js";
22
+ import { registerTestCleanup } from "./test-context.js";
23
+ /**
24
+ * The per-test context. `cleanup` is always present; capability plugins add
25
+ * `assert`, `client`, `db`, … by augmenting this interface (declaration
26
+ * merging) alongside a matching {@link TestContextRegistry} registration.
27
+ */
28
+ /**
29
+ * A CLASS (not an interface) so plugins can add REQUIRED properties via
30
+ * declaration merging — `declare module "@c9up/helix" { interface TestContext {
31
+ * client: ApiClient } }` — without breaking the core's construction (an external
32
+ * augmentation is not subject to helix's `strictPropertyInitialization`). This
33
+ * is exactly how Japa's `TestContext` works.
34
+ */
35
+ export class TestContext {
36
+ /**
37
+ * Add a shared property to every test context — Japa's
38
+ * `TestContext.macro('sleep', fn)`, callable on the class itself so a Japa
39
+ * plugin's registration code ports over unchanged.
40
+ */
41
+ static macro(name, value) {
42
+ TestContextRegistry.macro(name, value);
43
+ }
44
+ /** Add a lazily-computed, per-context property (Japa `TestContext.getter`). */
45
+ static getter(name, fn) {
46
+ TestContextRegistry.getter(name, fn);
47
+ }
48
+ /** Chai-flavored assertions (`@japa/assert` parity), alongside `expect`. */
49
+ assert;
50
+ /** The running test's own instance — name, options, dataset (Japa `ctx.test`). */
51
+ test;
52
+ constructor(test) {
53
+ this.assert = createAssert();
54
+ this.test = test;
55
+ }
56
+ /**
57
+ * Register a teardown that runs at the end of THIS test, regardless of
58
+ * outcome (Japa `ctx.cleanup`). Reverse-insertion order, isolated failures.
59
+ */
60
+ cleanup(fn) {
61
+ // Falls through to the active per-test frame; a false return means we were
62
+ // called outside a frame (defensive — the runner always wraps).
63
+ registerTestCleanup(fn);
64
+ }
65
+ }
66
+ const macros = new Map();
67
+ const getters = new Map();
68
+ /**
69
+ * Registry through which plugins extend the test context at runtime. Mirrors
70
+ * Japa's `TestContext.macro(name, value)` / `TestContext.getter(name, fn)`.
71
+ * Pair each call with a `declare module` augmentation for the types.
72
+ */
73
+ export const TestContextRegistry = {
74
+ /** Add a shared property present on every test context. */
75
+ macro(name, value) {
76
+ macros.set(name, value);
77
+ },
78
+ /**
79
+ * Add a lazily-computed, per-context property. The getter runs the first
80
+ * time the property is read on a given context (inside that test's frame),
81
+ * and the result is cached for that context.
82
+ */
83
+ getter(name, fn) {
84
+ getters.set(name, fn);
85
+ },
86
+ /** Remove a macro/getter (plugin teardown, tests). No arg clears all. */
87
+ clear(name) {
88
+ if (name === undefined) {
89
+ macros.clear();
90
+ getters.clear();
91
+ }
92
+ else {
93
+ macros.delete(name);
94
+ getters.delete(name);
95
+ }
96
+ },
97
+ /** Whether any extension is registered under `name`. */
98
+ has(name) {
99
+ return macros.has(name) || getters.has(name);
100
+ },
101
+ };
102
+ /**
103
+ * Build a fresh `TestContext` for one test attempt. MUST be called inside the
104
+ * per-test frame (`withTestContext`) so `ctx.cleanup` reaches the right frame.
105
+ * Applies registered macros (own value), then getters (lazy, cached per
106
+ * context). Dynamic props are attached via `defineProperty` so no `cleanup`
107
+ * override and no cast are needed.
108
+ */
109
+ export function buildTestContext(test) {
110
+ const ctx = new TestContext(test);
111
+ // `cleanup` and `test` are structural — the runtime hands them to the body
112
+ // and nothing may take their place. `assert` is NOT: helix ships one, and a
113
+ // project installing `@japa/assert` is asking for that one instead. Refusing
114
+ // the override left the plugin registered but never reached, which is how
115
+ // `plugins: [assert()]` could look wired and do nothing.
116
+ const reserved = new Set(["cleanup", "test"]);
117
+ for (const [name, value] of macros) {
118
+ if (reserved.has(name))
119
+ continue;
120
+ Object.defineProperty(ctx, name, {
121
+ value,
122
+ enumerable: true,
123
+ configurable: true,
124
+ writable: true,
125
+ });
126
+ }
127
+ for (const [name, fn] of getters) {
128
+ if (reserved.has(name))
129
+ continue;
130
+ let computed = false;
131
+ let cached;
132
+ Object.defineProperty(ctx, name, {
133
+ enumerable: true,
134
+ configurable: true,
135
+ get() {
136
+ if (!computed) {
137
+ cached = fn.call(ctx, ctx);
138
+ computed = true;
139
+ }
140
+ return cached;
141
+ },
142
+ });
143
+ }
144
+ return ctx;
145
+ }
146
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"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"}