@mandujs/core 0.23.0 → 0.25.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/package.json +3 -1
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -0
- package/src/bundler/build.ts +26 -3
- package/src/bundler/dev.ts +306 -4
- package/src/bundler/reverse-import-graph.ts +339 -0
- package/src/bundler/safe-build.test.ts +54 -0
- package/src/bundler/safe-build.ts +33 -7
- package/src/client/globals.ts +61 -44
- package/src/client/prefetch-helper.ts +55 -0
- package/src/client/router.ts +93 -15
- package/src/client/use-fetch.ts +243 -239
- package/src/config/mandu.ts +280 -172
- package/src/config/validate.ts +411 -357
- package/src/content/collection.ts +506 -0
- package/src/content/frontmatter.ts +189 -0
- package/src/content/generate-types.ts +168 -0
- package/src/content/index.ts +206 -168
- package/src/content/llms-txt.ts +196 -0
- package/src/content/prebuild.test.ts +249 -0
- package/src/content/prebuild.ts +400 -0
- package/src/content/schema.ts +20 -0
- package/src/content/sidebar.ts +212 -0
- package/src/content/slug.ts +110 -0
- package/src/guard/check.ts +5 -2
- package/src/observability/index.ts +37 -18
- package/src/observability/metrics.ts +334 -0
- package/src/runtime/adapter-bun.ts +64 -62
- package/src/runtime/index.ts +11 -0
- package/src/runtime/registry.ts +171 -0
- package/src/runtime/server.ts +214 -8
- package/src/runtime/ssr.ts +264 -8
- package/src/runtime/streaming-ssr.ts +100 -4
- package/src/utils/__tests__/lru-cache.test.ts +186 -0
- package/src/utils/lru-cache.ts +172 -75
package/src/config/mandu.ts
CHANGED
|
@@ -1,172 +1,280 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { readJsonFile } from "../utils/bun";
|
|
3
|
-
import type { ManduAdapter } from "../runtime/adapter";
|
|
4
|
-
import type { ManduPlugin, ManduHooks } from "../plugins/hooks";
|
|
5
|
-
|
|
6
|
-
export type GuardRuleSeverity = "error" | "warn" | "warning" | "off";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Test block configuration (Phase 12.1 — testing ecosystem).
|
|
10
|
-
*
|
|
11
|
-
* Shapes the CLI `mandu test` command's discovery, fixture, and reporter
|
|
12
|
-
* behaviour. All fields are optional; omitting the block yields sensible
|
|
13
|
-
* defaults that match Next.js / SvelteKit user expectations:
|
|
14
|
-
*
|
|
15
|
-
* - unit → `**\/*.test.ts` / `**\/*.test.tsx`, 30s timeout
|
|
16
|
-
* - integration → `tests/integration/**\/*.test.ts`, in-memory fixtures
|
|
17
|
-
* - e2e → reserved for Phase 12.2 (ATE integration)
|
|
18
|
-
* - coverage → reserved for Phase 12.3 (bun + playwright merge)
|
|
19
|
-
*/
|
|
20
|
-
export interface TestUnitConfig {
|
|
21
|
-
/** Glob patterns for unit test files. Default: `["**\/*.test.ts", "**\/*.test.tsx"]`. */
|
|
22
|
-
include?: string[];
|
|
23
|
-
/** Glob patterns to exclude (applied after `include`). Default: `["node_modules/**", ".mandu/**", "dist/**"]`. */
|
|
24
|
-
exclude?: string[];
|
|
25
|
-
/** Per-test timeout in milliseconds. Default: `30_000` (30s). */
|
|
26
|
-
timeout?: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface TestIntegrationConfig {
|
|
30
|
-
/** Glob patterns for integration test files. Default: `["tests/integration/**\/*.test.ts"]`. */
|
|
31
|
-
include?: string[];
|
|
32
|
-
/** Glob patterns to exclude. Default: same as unit defaults. */
|
|
33
|
-
exclude?: string[];
|
|
34
|
-
/**
|
|
35
|
-
* Database URL for fixtures. Default: `"sqlite::memory:"` (in-memory SQLite).
|
|
36
|
-
* Accepts any Bun.sql-compatible URL — see `@mandujs/core/db` for the schema matrix.
|
|
37
|
-
*/
|
|
38
|
-
dbUrl?: string;
|
|
39
|
-
/**
|
|
40
|
-
* Session storage strategy for `createTestSession`.
|
|
41
|
-
* - `"memory"` (default): CookieSessionStorage with ephemeral secret
|
|
42
|
-
* - `"sqlite"`: bun:sqlite-backed (Phase 2.5 storage, requires Phase 4)
|
|
43
|
-
*/
|
|
44
|
-
sessionStore?: "memory" | "sqlite";
|
|
45
|
-
/** Per-test timeout. Default: `60_000` (60s — integration work is slower). */
|
|
46
|
-
timeout?: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface TestE2EConfig {
|
|
50
|
-
/** Reserved for Phase 12.2. Currently a typed placeholder. */
|
|
51
|
-
reserved?: true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface TestCoverageConfig {
|
|
55
|
-
/** Minimum line coverage percentage (0-100). Reserved for Phase 12.3. */
|
|
56
|
-
lines?: number;
|
|
57
|
-
/** Minimum branch coverage percentage (0-100). Reserved for Phase 12.3. */
|
|
58
|
-
branches?: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface TestConfig {
|
|
62
|
-
unit?: TestUnitConfig;
|
|
63
|
-
integration?: TestIntegrationConfig;
|
|
64
|
-
e2e?: TestE2EConfig;
|
|
65
|
-
coverage?: TestCoverageConfig;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface ManduConfig {
|
|
69
|
-
adapter?: ManduAdapter;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { readJsonFile } from "../utils/bun";
|
|
3
|
+
import type { ManduAdapter } from "../runtime/adapter";
|
|
4
|
+
import type { ManduPlugin, ManduHooks } from "../plugins/hooks";
|
|
5
|
+
|
|
6
|
+
export type GuardRuleSeverity = "error" | "warn" | "warning" | "off";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Test block configuration (Phase 12.1 — testing ecosystem).
|
|
10
|
+
*
|
|
11
|
+
* Shapes the CLI `mandu test` command's discovery, fixture, and reporter
|
|
12
|
+
* behaviour. All fields are optional; omitting the block yields sensible
|
|
13
|
+
* defaults that match Next.js / SvelteKit user expectations:
|
|
14
|
+
*
|
|
15
|
+
* - unit → `**\/*.test.ts` / `**\/*.test.tsx`, 30s timeout
|
|
16
|
+
* - integration → `tests/integration/**\/*.test.ts`, in-memory fixtures
|
|
17
|
+
* - e2e → reserved for Phase 12.2 (ATE integration)
|
|
18
|
+
* - coverage → reserved for Phase 12.3 (bun + playwright merge)
|
|
19
|
+
*/
|
|
20
|
+
export interface TestUnitConfig {
|
|
21
|
+
/** Glob patterns for unit test files. Default: `["**\/*.test.ts", "**\/*.test.tsx"]`. */
|
|
22
|
+
include?: string[];
|
|
23
|
+
/** Glob patterns to exclude (applied after `include`). Default: `["node_modules/**", ".mandu/**", "dist/**"]`. */
|
|
24
|
+
exclude?: string[];
|
|
25
|
+
/** Per-test timeout in milliseconds. Default: `30_000` (30s). */
|
|
26
|
+
timeout?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TestIntegrationConfig {
|
|
30
|
+
/** Glob patterns for integration test files. Default: `["tests/integration/**\/*.test.ts"]`. */
|
|
31
|
+
include?: string[];
|
|
32
|
+
/** Glob patterns to exclude. Default: same as unit defaults. */
|
|
33
|
+
exclude?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Database URL for fixtures. Default: `"sqlite::memory:"` (in-memory SQLite).
|
|
36
|
+
* Accepts any Bun.sql-compatible URL — see `@mandujs/core/db` for the schema matrix.
|
|
37
|
+
*/
|
|
38
|
+
dbUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Session storage strategy for `createTestSession`.
|
|
41
|
+
* - `"memory"` (default): CookieSessionStorage with ephemeral secret
|
|
42
|
+
* - `"sqlite"`: bun:sqlite-backed (Phase 2.5 storage, requires Phase 4)
|
|
43
|
+
*/
|
|
44
|
+
sessionStore?: "memory" | "sqlite";
|
|
45
|
+
/** Per-test timeout. Default: `60_000` (60s — integration work is slower). */
|
|
46
|
+
timeout?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TestE2EConfig {
|
|
50
|
+
/** Reserved for Phase 12.2. Currently a typed placeholder. */
|
|
51
|
+
reserved?: true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TestCoverageConfig {
|
|
55
|
+
/** Minimum line coverage percentage (0-100). Reserved for Phase 12.3. */
|
|
56
|
+
lines?: number;
|
|
57
|
+
/** Minimum branch coverage percentage (0-100). Reserved for Phase 12.3. */
|
|
58
|
+
branches?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TestConfig {
|
|
62
|
+
unit?: TestUnitConfig;
|
|
63
|
+
integration?: TestIntegrationConfig;
|
|
64
|
+
e2e?: TestE2EConfig;
|
|
65
|
+
coverage?: TestCoverageConfig;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ManduConfig {
|
|
69
|
+
adapter?: ManduAdapter;
|
|
70
|
+
/**
|
|
71
|
+
* Issue #192 — Enable CSS View Transitions for cross-document
|
|
72
|
+
* navigations. When `true` (default) Mandu injects
|
|
73
|
+
* `<style>@view-transition { navigation: auto; }</style>` into the SSR
|
|
74
|
+
* `<head>`, which lets supporting browsers (Chrome/Edge ≥ 111) play a
|
|
75
|
+
* crossfade between the outgoing and incoming pages. Non-supporting
|
|
76
|
+
* browsers ignore the at-rule and fall back to the classic
|
|
77
|
+
* full-reload — zero regression.
|
|
78
|
+
*
|
|
79
|
+
* Set to `false` to opt out entirely (e.g. if your app ships a
|
|
80
|
+
* hand-rolled navigation animation or a conflicting CSS rule).
|
|
81
|
+
*
|
|
82
|
+
* Default: `true`.
|
|
83
|
+
*/
|
|
84
|
+
transitions?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Issue #192 — Enable hover-based link prefetch. When `true` (default)
|
|
87
|
+
* Mandu injects a ~500-byte inline script that listens for `mouseover`
|
|
88
|
+
* events on internal links (`<a href="/...">`) and issues a
|
|
89
|
+
* `<link rel="prefetch">` for each unique target. The browser's HTTP
|
|
90
|
+
* cache services the subsequent navigation, removing most of the TTFB
|
|
91
|
+
* for above-the-fold links.
|
|
92
|
+
*
|
|
93
|
+
* Per-link opt-out: add `data-no-prefetch` to an `<a>` tag to skip it.
|
|
94
|
+
* Global opt-out: set this field to `false`.
|
|
95
|
+
*
|
|
96
|
+
* Default: `true`.
|
|
97
|
+
*/
|
|
98
|
+
prefetch?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Issue #193 — Enable opt-out SPA navigation. When `true` (default)
|
|
101
|
+
* Mandu intercepts every internal same-origin `<a href="/...">` click
|
|
102
|
+
* and routes it through the client-side router, using the View
|
|
103
|
+
* Transitions API where available for a zero-flash experience. Plain
|
|
104
|
+
* `<a href="/about">` tags "just work" without a component wrapper.
|
|
105
|
+
*
|
|
106
|
+
* Escape hatches (the anchor always falls through to the browser):
|
|
107
|
+
* - Per-link opt-out: `data-no-spa` on the `<a>` tag.
|
|
108
|
+
* - External / cross-origin `href`.
|
|
109
|
+
* - `mailto:` / `tel:` / `javascript:` / etc. (non-http schemes).
|
|
110
|
+
* - `target="_blank"` (any `target` other than `_self`).
|
|
111
|
+
* - `download` attribute.
|
|
112
|
+
* - Modifier keys (Ctrl / Cmd / Shift / Alt) or middle/right-click.
|
|
113
|
+
*
|
|
114
|
+
* Global opt-out: set this field to `false` to revert to the legacy
|
|
115
|
+
* opt-in behavior, where only `<a data-mandu-link href="/...">` is
|
|
116
|
+
* intercepted. This is a breaking-change escape hatch for projects
|
|
117
|
+
* that relied on the pre-v0.22 default.
|
|
118
|
+
*
|
|
119
|
+
* Default: `true`.
|
|
120
|
+
*/
|
|
121
|
+
spa?: boolean;
|
|
122
|
+
server?: {
|
|
123
|
+
port?: number;
|
|
124
|
+
hostname?: string;
|
|
125
|
+
cors?:
|
|
126
|
+
| boolean
|
|
127
|
+
| {
|
|
128
|
+
origin?: string | string[];
|
|
129
|
+
methods?: string[];
|
|
130
|
+
credentials?: boolean;
|
|
131
|
+
};
|
|
132
|
+
streaming?: boolean;
|
|
133
|
+
rateLimit?:
|
|
134
|
+
| boolean
|
|
135
|
+
| {
|
|
136
|
+
windowMs?: number;
|
|
137
|
+
max?: number;
|
|
138
|
+
message?: string;
|
|
139
|
+
statusCode?: number;
|
|
140
|
+
headers?: boolean;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
guard?: {
|
|
144
|
+
preset?: "mandu" | "fsd" | "clean" | "hexagonal" | "atomic" | "cqrs";
|
|
145
|
+
srcDir?: string;
|
|
146
|
+
exclude?: string[];
|
|
147
|
+
realtime?: boolean;
|
|
148
|
+
rules?: Record<string, GuardRuleSeverity>;
|
|
149
|
+
contractRequired?: GuardRuleSeverity;
|
|
150
|
+
};
|
|
151
|
+
build?: {
|
|
152
|
+
outDir?: string;
|
|
153
|
+
minify?: boolean;
|
|
154
|
+
sourcemap?: boolean;
|
|
155
|
+
splitting?: boolean;
|
|
156
|
+
};
|
|
157
|
+
dev?: {
|
|
158
|
+
hmr?: boolean;
|
|
159
|
+
watchDirs?: string[];
|
|
160
|
+
/** Observability SQLite 영구 저장 (기본: true) */
|
|
161
|
+
observability?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Issue #191 — Dev-only `_devtools.js` (~1.15 MB React dev runtime +
|
|
164
|
+
* Mandu Kitchen panel) injection override.
|
|
165
|
+
*
|
|
166
|
+
* - `true` → force inject on every page (SSR-only projects that
|
|
167
|
+
* still want the Kitchen panel in dev).
|
|
168
|
+
* - `false` → force skip on every page (Kitchen-off dev loop).
|
|
169
|
+
* - `undefined` → default. Inject iff the page's bundle manifest
|
|
170
|
+
* has at least one island. Pure-SSR pages download
|
|
171
|
+
* zero devtools bytes.
|
|
172
|
+
*
|
|
173
|
+
* Production builds never emit `_devtools.js`, so this flag is
|
|
174
|
+
* a no-op in prod regardless of value.
|
|
175
|
+
*/
|
|
176
|
+
devtools?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Issue #196 — Auto-run `scripts/prebuild-*.ts` before `mandu dev`
|
|
179
|
+
* boots, and re-run them when files under `contentDir` change in
|
|
180
|
+
* watch mode.
|
|
181
|
+
*
|
|
182
|
+
* - `true` → always run discovered prebuild scripts, regardless
|
|
183
|
+
* of whether `content/` exists (useful for projects
|
|
184
|
+
* that ship generators that write outside `content/`).
|
|
185
|
+
* - `false` → never auto-run. User stays responsible for the
|
|
186
|
+
* chain (`bun scripts/prebuild-*.ts && mandu dev`).
|
|
187
|
+
* - `undefined` → default. Auto-enabled iff the project has a
|
|
188
|
+
* `content/` directory OR at least one
|
|
189
|
+
* `scripts/prebuild-*.ts`. Silent no-op otherwise.
|
|
190
|
+
*
|
|
191
|
+
* See `@mandujs/core/content/prebuild` for the discovery + execution
|
|
192
|
+
* contract.
|
|
193
|
+
*/
|
|
194
|
+
autoPrebuild?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Issue #196 — Directory whose changes trigger a watch-mode
|
|
197
|
+
* prebuild re-run. Defaults to `"content"`. Ignored when
|
|
198
|
+
* `autoPrebuild === false`. Relative to project root.
|
|
199
|
+
*/
|
|
200
|
+
contentDir?: string;
|
|
201
|
+
};
|
|
202
|
+
fsRoutes?: {
|
|
203
|
+
routesDir?: string;
|
|
204
|
+
extensions?: string[];
|
|
205
|
+
exclude?: string[];
|
|
206
|
+
islandSuffix?: string;
|
|
207
|
+
};
|
|
208
|
+
seo?: {
|
|
209
|
+
enabled?: boolean;
|
|
210
|
+
defaultTitle?: string;
|
|
211
|
+
titleTemplate?: string;
|
|
212
|
+
};
|
|
213
|
+
/** Phase 12.1 — `mandu test` configuration block. */
|
|
214
|
+
test?: TestConfig;
|
|
215
|
+
/**
|
|
216
|
+
* Phase 17 — observability endpoint toggles.
|
|
217
|
+
*
|
|
218
|
+
* Both fields default to `undefined`, which means "use mode default":
|
|
219
|
+
* - dev mode → endpoint exposed
|
|
220
|
+
* - prod mode → endpoint hidden unless `MANDU_DEBUG_HEAP=1`
|
|
221
|
+
*
|
|
222
|
+
* Explicit `true` / `false` overrides the mode default, so operators
|
|
223
|
+
* can opt-in to exposing metrics in prod (for a trusted internal
|
|
224
|
+
* network) or opt-out in dev (for a clean test harness).
|
|
225
|
+
*/
|
|
226
|
+
observability?: {
|
|
227
|
+
/** `/_mandu/heap` JSON exposure toggle. */
|
|
228
|
+
heapEndpoint?: boolean;
|
|
229
|
+
/** `/_mandu/metrics` Prometheus text exposure toggle. */
|
|
230
|
+
metricsEndpoint?: boolean;
|
|
231
|
+
};
|
|
232
|
+
plugins?: ManduPlugin[];
|
|
233
|
+
hooks?: Partial<ManduHooks>;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const CONFIG_FILES = [
|
|
237
|
+
"mandu.config.ts",
|
|
238
|
+
"mandu.config.js",
|
|
239
|
+
"mandu.config.json",
|
|
240
|
+
path.join(".mandu", "guard.json"),
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
export function coerceConfig(raw: unknown, source: string): ManduConfig {
|
|
244
|
+
if (!raw || typeof raw !== "object") return {};
|
|
245
|
+
|
|
246
|
+
// .mandu/guard.json can be guard-only
|
|
247
|
+
if (source.endsWith("guard.json") && !("guard" in (raw as Record<string, unknown>))) {
|
|
248
|
+
return { guard: raw as ManduConfig["guard"] };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return raw as ManduConfig;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export async function loadManduConfig(rootDir: string): Promise<ManduConfig> {
|
|
255
|
+
for (const fileName of CONFIG_FILES) {
|
|
256
|
+
const filePath = path.join(rootDir, fileName);
|
|
257
|
+
if (!(await Bun.file(filePath).exists())) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (fileName.endsWith(".json")) {
|
|
262
|
+
try {
|
|
263
|
+
const parsed = await readJsonFile(filePath);
|
|
264
|
+
return coerceConfig(parsed, fileName);
|
|
265
|
+
} catch {
|
|
266
|
+
return {};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const module = await import(filePath);
|
|
272
|
+
const raw = module?.default ?? module;
|
|
273
|
+
return coerceConfig(raw, fileName);
|
|
274
|
+
} catch {
|
|
275
|
+
return {};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {};
|
|
280
|
+
}
|