@mandujs/core 0.41.2 → 0.43.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.
Files changed (88) hide show
  1. package/package.json +21 -4
  2. package/src/auth/__tests__/login.test.ts +420 -419
  3. package/src/auth/__tests__/reset.test.ts +296 -296
  4. package/src/brain/adapters/anthropic-oauth.ts +421 -420
  5. package/src/brain/adapters/index.ts +2 -1
  6. package/src/brain/adapters/ollama.ts +1 -1
  7. package/src/brain/adapters/openai-oauth.ts +534 -533
  8. package/src/brain/brain.ts +2 -1
  9. package/src/brain/redactor.ts +196 -196
  10. package/src/bundler/__tests__/cli-bench-utils.test.ts +149 -149
  11. package/src/bundler/__tests__/cold-start.test.ts +504 -504
  12. package/src/bundler/__tests__/fast-refresh.test.ts +607 -606
  13. package/src/bundler/__tests__/hdr.test.ts +1 -1
  14. package/src/bundler/analyzer.ts +958 -958
  15. package/src/bundler/build.ts +104 -14
  16. package/src/bundler/dev.ts +125 -0
  17. package/src/bundler/hmr-types.ts +1 -0
  18. package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -0
  19. package/src/bundler/plugins/index.ts +14 -0
  20. package/src/bundler/plugins/react-compiler-lint.ts +253 -0
  21. package/src/bundler/plugins/react-compiler.ts +162 -0
  22. package/src/bundler/types.ts +12 -0
  23. package/src/change/integrity.ts +2 -1
  24. package/src/client/index.ts +10 -0
  25. package/src/client/island.ts +38 -11
  26. package/src/client/router.ts +6 -1
  27. package/src/config/mandu.ts +57 -0
  28. package/src/config/validate.ts +42 -0
  29. package/src/content/collection.ts +844 -809
  30. package/src/content/content-layer.ts +316 -314
  31. package/src/content/content.test.ts +433 -433
  32. package/src/content/digest.ts +133 -133
  33. package/src/content/generate-types.ts +168 -168
  34. package/src/content/index.ts +6 -1
  35. package/src/content/llms-txt.ts +277 -277
  36. package/src/contract/define.ts +474 -474
  37. package/src/contract/route-helpers.ts +2 -1
  38. package/src/contract/zod-utils.ts +158 -155
  39. package/src/db/index.ts +513 -513
  40. package/src/desktop/__tests__/smoke.test.ts +100 -100
  41. package/src/desktop/webview-fallback.ts +583 -583
  42. package/src/desktop/window.ts +3 -1
  43. package/src/dev-error-overlay/overlay-client.ts +300 -300
  44. package/src/devtools/ai/mcp-connector.ts +499 -498
  45. package/src/devtools/client/components/kitchen-root.tsx +7 -2
  46. package/src/email/resend.ts +163 -163
  47. package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -0
  48. package/src/guard/ast-analyzer.ts +806 -806
  49. package/src/guard/graph.ts +898 -898
  50. package/src/guard/index.ts +16 -0
  51. package/src/guard/statistics.ts +578 -578
  52. package/src/guard/tsgolint-bridge.ts +512 -0
  53. package/src/i18n/locale-resolver.ts +214 -214
  54. package/src/id/__tests__/id.test.ts +120 -120
  55. package/src/intent/index.ts +321 -321
  56. package/src/island/index.ts +39 -23
  57. package/src/kitchen/api/contract-api.ts +15 -8
  58. package/src/kitchen/kitchen-ui.ts +2137 -2137
  59. package/src/lockfile/index.ts +3 -2
  60. package/src/middleware/oauth/__tests__/oauth.test.ts +575 -574
  61. package/src/middleware/rate-limit/__tests__/rate-limit.test.ts +642 -642
  62. package/src/middleware/secure/index.ts +417 -417
  63. package/src/observability/event-bus.ts +2 -2
  64. package/src/observability/metrics.ts +334 -334
  65. package/src/observability/tracing.ts +694 -694
  66. package/src/openapi/generator.ts +1 -1
  67. package/src/perf/user-marks.ts +553 -553
  68. package/src/plugins/registry.ts +387 -387
  69. package/src/resource/ddl/diff.ts +392 -392
  70. package/src/resource/ddl/snapshot.ts +448 -447
  71. package/src/resource/generator-schema.ts +477 -476
  72. package/src/resource/parser.ts +4 -2
  73. package/src/resource/schema.ts +1 -1
  74. package/src/router/fs-patterns.ts +422 -422
  75. package/src/runtime/fast-refresh-types.ts +126 -128
  76. package/src/runtime/image-handler.ts +206 -195
  77. package/src/runtime/router.test.ts +476 -476
  78. package/src/runtime/security.ts +155 -155
  79. package/src/runtime/server.ts +36 -19
  80. package/src/runtime/session-key.ts +328 -328
  81. package/src/scheduler/__tests__/scheduler.test.ts +514 -514
  82. package/src/seo/resolve/index.ts +353 -353
  83. package/src/spec/load.ts +1 -1
  84. package/src/testing/reporter.ts +676 -676
  85. package/src/testing/server.ts +196 -196
  86. package/src/testing/snapshot.ts +444 -444
  87. package/src/utils/__tests__/lru-cache.test.ts +186 -186
  88. package/src/utils/bun.ts +8 -8
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import type { ComponentType, ReactNode } from 'react';
16
- import { z } from 'zod';
16
+ import type { z } from 'zod';
17
17
 
18
18
  // ============================================================================
19
19
  // Types
@@ -58,20 +58,30 @@ export type IslandComponent<P = unknown> = ComponentType<P> & IslandMeta;
58
58
  // Island Registry (서버/클라이언트 공용)
59
59
  // ============================================================================
60
60
 
61
- const islandRegistry = new Map<string, IslandComponent<any>>();
61
+ // Registry bag for heterogeneous React components. React's `ComponentType<P>`
62
+ // is contravariant on `P` — a `ComponentType<{id:string}>` is NOT assignable
63
+ // to `ComponentType<Record<string, unknown>>`. The correct bag type for a
64
+ // heterogeneous component map is `ComponentType<any>`; narrowing happens at
65
+ // call sites via each island's own typed `IslandComponent<P>` signature.
66
+ // Same pattern used in client/island.ts `createPartialGroup()`.
67
+ // oxlint-disable no-explicit-any -- heterogeneous React component registry
68
+ type AnyIslandComponent = IslandComponent<any>;
69
+
70
+ const islandRegistry = new Map<string, AnyIslandComponent>();
62
71
  let islandCounter = 0;
63
72
 
64
- export function registerIsland(name: string, component: IslandComponent<any>): void {
73
+ export function registerIsland(name: string, component: AnyIslandComponent): void {
65
74
  islandRegistry.set(name, component);
66
75
  }
67
76
 
68
- export function getIsland(name: string): IslandComponent<any> | undefined {
77
+ export function getIsland(name: string): AnyIslandComponent | undefined {
69
78
  return islandRegistry.get(name);
70
79
  }
71
80
 
72
- export function getAllIslands(): Map<string, IslandComponent<any>> {
81
+ export function getAllIslands(): Map<string, AnyIslandComponent> {
73
82
  return islandRegistry;
74
83
  }
84
+ // oxlint-enable no-explicit-any
75
85
 
76
86
  // ============================================================================
77
87
  // Client Island Types (setup/render 패턴)
@@ -98,14 +108,10 @@ export interface CompiledClientIsland<TServerData, TSetupResult> {
98
108
 
99
109
  /** Type guard: is this a ClientIslandDefinition? */
100
110
  function isClientIslandDefinition(arg: unknown): arg is ClientIslandDefinition<unknown, unknown> {
101
- return (
102
- arg !== null &&
103
- typeof arg === 'object' &&
104
- 'setup' in arg &&
105
- 'render' in arg &&
106
- typeof (arg as any).setup === 'function' &&
107
- typeof (arg as any).render === 'function'
108
- );
111
+ if (arg === null || typeof arg !== 'object') return false;
112
+ if (!('setup' in arg) || !('render' in arg)) return false;
113
+ const candidate = arg as { setup?: unknown; render?: unknown };
114
+ return typeof candidate.setup === 'function' && typeof candidate.render === 'function';
109
115
  }
110
116
 
111
117
  // ============================================================================
@@ -140,6 +146,15 @@ function isClientIslandDefinition(arg: unknown): arg is ClientIslandDefinition<u
140
146
  * });
141
147
  */
142
148
 
149
+ // Using `unknown` generic arguments on the internal impl types keeps the
150
+ // overloads below as the single source of truth for external type inference
151
+ // while avoiding `any` in the implementation. Callers always hit one of
152
+ // the overloads and never see this input/output type.
153
+ type IslandImplInput =
154
+ | IslandHydrationStrategy
155
+ | IslandOptions<unknown>
156
+ | ClientIslandDefinition<unknown, unknown>;
157
+
143
158
  // Overload 1: Setup/Render client island pattern
144
159
  export function island<TServerData, TSetupResult = TServerData>(
145
160
  definition: ClientIslandDefinition<TServerData, TSetupResult>
@@ -159,9 +174,9 @@ export function island<P extends Record<string, unknown>>(
159
174
 
160
175
  // Implementation
161
176
  export function island(
162
- strategyOrOptionsOrDefinition: IslandHydrationStrategy | IslandOptions<any> | ClientIslandDefinition<any, any>,
163
- Component?: ComponentType<any>
164
- ): IslandComponent<any> | CompiledClientIsland<any, any> {
177
+ strategyOrOptionsOrDefinition: IslandImplInput,
178
+ Component?: ComponentType<unknown>,
179
+ ): AnyIslandComponent | CompiledClientIsland<unknown, unknown> {
165
180
  // Pattern 2: Setup/Render client island
166
181
  if (Component === undefined && isClientIslandDefinition(strategyOrOptionsOrDefinition)) {
167
182
  return {
@@ -175,20 +190,21 @@ export function island(
175
190
  throw new Error('[Mandu Island] Component is required for declarative island pattern');
176
191
  }
177
192
 
178
- const options: IslandOptions<any> = typeof strategyOrOptionsOrDefinition === 'string'
193
+ const options: IslandOptions<unknown> = typeof strategyOrOptionsOrDefinition === 'string'
179
194
  ? { hydrate: strategyOrOptionsOrDefinition }
180
- : strategyOrOptionsOrDefinition as IslandOptions<any>;
195
+ : (strategyOrOptionsOrDefinition as IslandOptions<unknown>);
181
196
 
182
197
  const name = options.name || `island_${++islandCounter}_${Component.name || 'Anonymous'}`;
183
198
 
184
- // Island 메타데이터 부착
185
- const IslandWrapper = Component as IslandComponent<any>;
199
+ // Island 메타데이터 부착 — mutate the component in-place so the caller's
200
+ // reference carries the attached `IslandMeta` fields.
201
+ const IslandWrapper = Component as unknown as AnyIslandComponent;
186
202
  IslandWrapper.__island = true;
187
203
  IslandWrapper.__hydrate = options.hydrate;
188
204
  IslandWrapper.__media = options.media;
189
205
  IslandWrapper.__fallback = options.fallback;
190
206
  IslandWrapper.__name = name;
191
- IslandWrapper.__propsSchema = options.props;
207
+ IslandWrapper.__propsSchema = options.props as IslandMeta['__propsSchema'];
192
208
 
193
209
  // 레지스트리에 등록
194
210
  registerIsland(name, IslandWrapper);
@@ -200,10 +216,10 @@ export function island(
200
216
  // isIsland() - Island 컴포넌트 체크
201
217
  // ============================================================================
202
218
 
203
- export function isIsland(component: unknown): component is IslandComponent<any> {
219
+ export function isIsland(component: unknown): component is AnyIslandComponent {
204
220
  return (
205
221
  typeof component === 'function' &&
206
- (component as IslandComponent<any>).__island === true
222
+ (component as Partial<IslandMeta>).__island === true
207
223
  );
208
224
  }
209
225
 
@@ -9,6 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { RoutesManifest, RouteSpec } from "../../spec/schema";
12
+ import type { ContractSchema, ResponseSchemaWithExamples } from "../../contract/schema";
13
+ import type { z } from "zod";
12
14
  import {
13
15
  generateOpenAPIDocument,
14
16
  zodToOpenAPISchema,
@@ -93,13 +95,18 @@ export class ContractPlaygroundAPI {
93
95
  schemas[method] = schema;
94
96
  }
95
97
 
96
- // Response schemas
98
+ // Response schemas. Contract response entries are either a raw Zod
99
+ // schema or a `ResponseSchemaWithExamples` wrapper; narrow on the
100
+ // presence of `.schema` rather than widening to `any`.
97
101
  const responseSchemas: Record<string, unknown> = {};
98
102
  for (const [status, resSchema] of Object.entries(contract.response || {})) {
99
- if (resSchema && typeof resSchema === "object" && "schema" in resSchema) {
100
- responseSchemas[status] = zodToOpenAPISchema((resSchema as any).schema);
101
- } else if (resSchema) {
102
- responseSchemas[status] = zodToOpenAPISchema(resSchema as any);
103
+ if (!resSchema) continue;
104
+ if (typeof resSchema === "object" && "schema" in resSchema) {
105
+ responseSchemas[status] = zodToOpenAPISchema(
106
+ (resSchema as ResponseSchemaWithExamples).schema,
107
+ );
108
+ } else {
109
+ responseSchemas[status] = zodToOpenAPISchema(resSchema as z.ZodTypeAny);
103
110
  }
104
111
  }
105
112
 
@@ -212,12 +219,12 @@ export class ContractPlaygroundAPI {
212
219
 
213
220
  // ────────────────────────────────────────────────
214
221
 
215
- private async loadContract(route: RouteSpec): Promise<any | null> {
222
+ private async loadContract(route: RouteSpec): Promise<ContractSchema | null> {
216
223
  if (!route.contractModule) return null;
217
224
  try {
218
225
  const fullPath = path.join(this.rootDir, route.contractModule);
219
- const module = await import(fullPath);
220
- return module.default;
226
+ const module = (await import(fullPath)) as { default?: ContractSchema };
227
+ return module.default ?? null;
221
228
  } catch {
222
229
  return null;
223
230
  }