@mars-stack/core 0.4.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 (192) hide show
  1. package/README.md +32 -0
  2. package/cursor/manifest.json +304 -0
  3. package/cursor/rules/mars-composition-patterns.mdc +186 -0
  4. package/cursor/rules/mars-data-access.mdc +26 -0
  5. package/cursor/rules/mars-project-structure.mdc +34 -0
  6. package/cursor/rules/mars-security.mdc +25 -0
  7. package/cursor/rules/mars-testing.mdc +24 -0
  8. package/cursor/rules/mars-ui-conventions.mdc +29 -0
  9. package/cursor/skills/mars-add-api-route/SKILL.md +120 -0
  10. package/cursor/skills/mars-add-audit-log/SKILL.md +373 -0
  11. package/cursor/skills/mars-add-blog/SKILL.md +447 -0
  12. package/cursor/skills/mars-add-command-palette/SKILL.md +438 -0
  13. package/cursor/skills/mars-add-component/SKILL.md +158 -0
  14. package/cursor/skills/mars-add-crud-routes/SKILL.md +221 -0
  15. package/cursor/skills/mars-add-e2e-test/SKILL.md +227 -0
  16. package/cursor/skills/mars-add-error-boundary/SKILL.md +472 -0
  17. package/cursor/skills/mars-add-feature/SKILL.md +174 -0
  18. package/cursor/skills/mars-add-middleware/SKILL.md +135 -0
  19. package/cursor/skills/mars-add-page/SKILL.md +153 -0
  20. package/cursor/skills/mars-add-prisma-model/SKILL.md +148 -0
  21. package/cursor/skills/mars-add-protected-resource/SKILL.md +192 -0
  22. package/cursor/skills/mars-add-role/SKILL.md +156 -0
  23. package/cursor/skills/mars-add-server-action/SKILL.md +167 -0
  24. package/cursor/skills/mars-add-webhook/SKILL.md +192 -0
  25. package/cursor/skills/mars-build-complete-feature/SKILL.md +228 -0
  26. package/cursor/skills/mars-build-dashboard/SKILL.md +211 -0
  27. package/cursor/skills/mars-build-data-table/SKILL.md +284 -0
  28. package/cursor/skills/mars-build-form/SKILL.md +229 -0
  29. package/cursor/skills/mars-build-landing-page/SKILL.md +248 -0
  30. package/cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  31. package/cursor/skills/mars-configure-ai/SKILL.md +617 -0
  32. package/cursor/skills/mars-configure-analytics/SKILL.md +413 -0
  33. package/cursor/skills/mars-configure-dark-mode/SKILL.md +309 -0
  34. package/cursor/skills/mars-configure-email/SKILL.md +170 -0
  35. package/cursor/skills/mars-configure-email-verification/SKILL.md +333 -0
  36. package/cursor/skills/mars-configure-feature-flags/SKILL.md +361 -0
  37. package/cursor/skills/mars-configure-i18n/SKILL.md +518 -0
  38. package/cursor/skills/mars-configure-jobs/SKILL.md +500 -0
  39. package/cursor/skills/mars-configure-magic-links/SKILL.md +385 -0
  40. package/cursor/skills/mars-configure-multi-tenancy/SKILL.md +611 -0
  41. package/cursor/skills/mars-configure-notifications/SKILL.md +569 -0
  42. package/cursor/skills/mars-configure-oauth/SKILL.md +217 -0
  43. package/cursor/skills/mars-configure-onboarding/SKILL.md +483 -0
  44. package/cursor/skills/mars-configure-payments/SKILL.md +243 -0
  45. package/cursor/skills/mars-configure-realtime/SKILL.md +733 -0
  46. package/cursor/skills/mars-configure-search/SKILL.md +581 -0
  47. package/cursor/skills/mars-configure-storage/SKILL.md +273 -0
  48. package/cursor/skills/mars-configure-two-factor/SKILL.md +518 -0
  49. package/cursor/skills/mars-create-execution-plan/SKILL.md +204 -0
  50. package/cursor/skills/mars-create-seed/SKILL.md +191 -0
  51. package/cursor/skills/mars-deploy-to-vercel/SKILL.md +300 -0
  52. package/cursor/skills/mars-design-tokens/SKILL.md +138 -0
  53. package/cursor/skills/mars-setup-billing/SKILL.md +322 -0
  54. package/cursor/skills/mars-setup-project/SKILL.md +104 -0
  55. package/cursor/skills/mars-setup-teams/SKILL.md +688 -0
  56. package/cursor/skills/mars-test-api-route/SKILL.md +219 -0
  57. package/cursor/skills/mars-update-architecture-docs/SKILL.md +189 -0
  58. package/dist/api-error/index.d.ts +27 -0
  59. package/dist/api-error/index.d.ts.map +1 -0
  60. package/dist/api-error/index.js +2 -0
  61. package/dist/auth/credential-tag.d.ts +5 -0
  62. package/dist/auth/credential-tag.d.ts.map +1 -0
  63. package/dist/auth/credential-tag.js +2 -0
  64. package/dist/auth/crypto-utils.d.ts +43 -0
  65. package/dist/auth/crypto-utils.d.ts.map +1 -0
  66. package/dist/auth/crypto-utils.js +1 -0
  67. package/dist/auth/csrf.d.ts +32 -0
  68. package/dist/auth/csrf.d.ts.map +1 -0
  69. package/dist/auth/csrf.js +2 -0
  70. package/dist/auth/hooks/index.d.ts +4 -0
  71. package/dist/auth/hooks/index.d.ts.map +1 -0
  72. package/dist/auth/hooks/index.js +68 -0
  73. package/dist/auth/hooks/useCSRF.d.ts +7 -0
  74. package/dist/auth/hooks/useCSRF.d.ts.map +1 -0
  75. package/dist/auth/hooks/usePasswordStrength.d.ts +17 -0
  76. package/dist/auth/hooks/usePasswordStrength.d.ts.map +1 -0
  77. package/dist/auth/internal-api-key.d.ts +5 -0
  78. package/dist/auth/internal-api-key.d.ts.map +1 -0
  79. package/dist/auth/internal-api-key.js +30 -0
  80. package/dist/auth/link-utils.d.ts +13 -0
  81. package/dist/auth/link-utils.d.ts.map +1 -0
  82. package/dist/auth/link-utils.js +1 -0
  83. package/dist/auth/middleware.d.ts +56 -0
  84. package/dist/auth/middleware.d.ts.map +1 -0
  85. package/dist/auth/middleware.js +3 -0
  86. package/dist/auth/password.d.ts +28 -0
  87. package/dist/auth/password.d.ts.map +1 -0
  88. package/dist/auth/password.js +1 -0
  89. package/dist/auth/reset-token.d.ts +3 -0
  90. package/dist/auth/reset-token.d.ts.map +1 -0
  91. package/dist/auth/reset-token.js +9 -0
  92. package/dist/auth/responses.d.ts +15 -0
  93. package/dist/auth/responses.d.ts.map +1 -0
  94. package/dist/auth/responses.js +2 -0
  95. package/dist/auth/session.d.ts +79 -0
  96. package/dist/auth/session.d.ts.map +1 -0
  97. package/dist/auth/session.js +1 -0
  98. package/dist/auth/types.d.ts +18 -0
  99. package/dist/auth/types.d.ts.map +1 -0
  100. package/dist/auth/types.js +10 -0
  101. package/dist/auth/validation.d.ts +146 -0
  102. package/dist/auth/validation.d.ts.map +1 -0
  103. package/dist/auth/validation.js +116 -0
  104. package/dist/auth/validators.d.ts +4 -0
  105. package/dist/auth/validators.d.ts.map +1 -0
  106. package/dist/auth/validators.js +27 -0
  107. package/dist/auth/verification.d.ts +54 -0
  108. package/dist/auth/verification.d.ts.map +1 -0
  109. package/dist/auth/verification.js +39 -0
  110. package/dist/chunk-4LS3QDD5.js +162 -0
  111. package/dist/chunk-ABBUHT5Z.js +110 -0
  112. package/dist/chunk-CTYAVMOF.js +15 -0
  113. package/dist/chunk-GVLH2GQP.js +14 -0
  114. package/dist/chunk-HOSMMQMA.js +109 -0
  115. package/dist/chunk-MXQ66RUN.js +28 -0
  116. package/dist/chunk-PZE3JGXO.js +149 -0
  117. package/dist/chunk-QAH2Y5WK.js +93 -0
  118. package/dist/chunk-QWMN5UJC.js +76 -0
  119. package/dist/chunk-ROQV54MU.js +117 -0
  120. package/dist/chunk-U4NZQ366.js +46 -0
  121. package/dist/chunk-WBJOIENS.js +22 -0
  122. package/dist/chunk-WO6FHJHG.js +29 -0
  123. package/dist/chunk-Z5BEKPJI.js +96 -0
  124. package/dist/chunk-ZA46T6GX.js +24 -0
  125. package/dist/configure-mars.d.ts +104 -0
  126. package/dist/configure-mars.d.ts.map +1 -0
  127. package/dist/database/index.d.ts +8 -0
  128. package/dist/database/index.d.ts.map +1 -0
  129. package/dist/database/index.js +1 -0
  130. package/dist/email/index.d.ts +25 -0
  131. package/dist/email/index.d.ts.map +1 -0
  132. package/dist/email/index.js +2 -0
  133. package/dist/email/types.d.ts +18 -0
  134. package/dist/email/types.d.ts.map +1 -0
  135. package/dist/env/index.d.ts +36 -0
  136. package/dist/env/index.d.ts.map +1 -0
  137. package/dist/env/index.js +1 -0
  138. package/dist/index.d.ts +6 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +163 -0
  141. package/dist/logger/index.d.ts +80 -0
  142. package/dist/logger/index.d.ts.map +1 -0
  143. package/dist/logger/index.js +1 -0
  144. package/dist/payments/index.d.ts +53 -0
  145. package/dist/payments/index.d.ts.map +1 -0
  146. package/dist/payments/index.js +72 -0
  147. package/dist/plugin/builtin/email-plugins.d.ts +10 -0
  148. package/dist/plugin/builtin/email-plugins.d.ts.map +1 -0
  149. package/dist/plugin/builtin/index.d.ts +4 -0
  150. package/dist/plugin/builtin/index.d.ts.map +1 -0
  151. package/dist/plugin/builtin/index.js +324 -0
  152. package/dist/plugin/builtin/payment-plugins.d.ts +4 -0
  153. package/dist/plugin/builtin/payment-plugins.d.ts.map +1 -0
  154. package/dist/plugin/builtin/storage-plugins.d.ts +5 -0
  155. package/dist/plugin/builtin/storage-plugins.d.ts.map +1 -0
  156. package/dist/plugin/index.d.ts +21 -0
  157. package/dist/plugin/index.d.ts.map +1 -0
  158. package/dist/plugin/index.js +30 -0
  159. package/dist/rate-limit/index.d.ts +89 -0
  160. package/dist/rate-limit/index.d.ts.map +1 -0
  161. package/dist/rate-limit/index.js +166 -0
  162. package/dist/seo/faq.d.ts +37 -0
  163. package/dist/seo/faq.d.ts.map +1 -0
  164. package/dist/seo/index.d.ts +75 -0
  165. package/dist/seo/index.d.ts.map +1 -0
  166. package/dist/seo/index.js +1 -0
  167. package/dist/storage/index.d.ts +50 -0
  168. package/dist/storage/index.d.ts.map +1 -0
  169. package/dist/storage/index.js +211 -0
  170. package/dist/test-utils/factories.d.ts +38 -0
  171. package/dist/test-utils/factories.d.ts.map +1 -0
  172. package/dist/test-utils/index.d.ts +6 -0
  173. package/dist/test-utils/index.d.ts.map +1 -0
  174. package/dist/test-utils/index.js +117 -0
  175. package/dist/test-utils/mock-auth.d.ts +25 -0
  176. package/dist/test-utils/mock-auth.d.ts.map +1 -0
  177. package/dist/test-utils/mock-prisma.d.ts +55 -0
  178. package/dist/test-utils/mock-prisma.d.ts.map +1 -0
  179. package/dist/test-utils/render.d.ts +4 -0
  180. package/dist/test-utils/render.d.ts.map +1 -0
  181. package/dist/test-utils/request-helpers.d.ts +6 -0
  182. package/dist/test-utils/request-helpers.d.ts.map +1 -0
  183. package/dist/types.d.ts +53 -0
  184. package/dist/types.d.ts.map +1 -0
  185. package/dist/utils/math.d.ts +2 -0
  186. package/dist/utils/math.d.ts.map +1 -0
  187. package/dist/utils/math.js +7 -0
  188. package/dist/utils/optional-import.d.ts +14 -0
  189. package/dist/utils/optional-import.d.ts.map +1 -0
  190. package/package.json +205 -0
  191. package/scripts/generate-skill-adapters.ts +146 -0
  192. package/scripts/postinstall.mjs +146 -0
@@ -0,0 +1,37 @@
1
+ export type FaqParagraphPart = {
2
+ type: 'text';
3
+ value: string;
4
+ } | {
5
+ type: 'link';
6
+ href: string;
7
+ text: string;
8
+ };
9
+ export type FaqAnswerBlock = {
10
+ type: 'paragraph';
11
+ parts: ReadonlyArray<FaqParagraphPart>;
12
+ } | {
13
+ type: 'unordered_list';
14
+ items: ReadonlyArray<string>;
15
+ } | {
16
+ type: 'ordered_list';
17
+ items: ReadonlyArray<string>;
18
+ };
19
+ export interface FaqItem {
20
+ question: string;
21
+ answer: ReadonlyArray<FaqAnswerBlock>;
22
+ }
23
+ export interface FaqSection {
24
+ heading: string;
25
+ items: ReadonlyArray<FaqItem>;
26
+ }
27
+ /**
28
+ * Converts structured FAQ answer blocks to a plain-text string.
29
+ * Paragraphs are collapsed to single lines; list items are joined with semicolons.
30
+ *
31
+ * @param blocks - Array of paragraph or list blocks from an FAQ answer
32
+ * @returns A newline-separated plain-text representation
33
+ * @example
34
+ * const text = toPlainTextFromFaqBlocks(faqItem.answer);
35
+ */
36
+ export declare function toPlainTextFromFaqBlocks(blocks: ReadonlyArray<FaqAnswerBlock>): string;
37
+ //# sourceMappingURL=faq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faq.d.ts","sourceRoot":"","sources":["../../src/seo/faq.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAE3D,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,MAAM,CAsBtF"}
@@ -0,0 +1,75 @@
1
+ export { toPlainTextFromFaqBlocks } from './faq';
2
+ export type { FaqParagraphPart, FaqAnswerBlock, FaqItem, FaqSection } from './faq';
3
+ export interface BreadcrumbItem {
4
+ name: string;
5
+ url: string;
6
+ }
7
+ export interface JsonLdFaqItem {
8
+ question: string;
9
+ answer: string;
10
+ }
11
+ export interface ArticleJsonLdInput {
12
+ title: string;
13
+ description: string;
14
+ slug: string;
15
+ date: string;
16
+ author: string;
17
+ tags: string[];
18
+ }
19
+ export interface SiteNavItem {
20
+ name: string;
21
+ url: string;
22
+ }
23
+ interface SeoConfig {
24
+ name: string;
25
+ url: string;
26
+ description: string;
27
+ supportEmail?: string;
28
+ }
29
+ /**
30
+ * Builds a standalone FAQ page JSON-LD structured data object.
31
+ *
32
+ * @param items - Array of question/answer pairs
33
+ * @returns A Schema.org FAQPage JSON-LD object
34
+ * @example
35
+ * const jsonLd = buildFaqPageJsonLd([{ question: 'What is Mars?', answer: 'A SaaS generator.' }]);
36
+ */
37
+ /**
38
+ * Builds a Schema.org FAQPage JSON-LD object from question/answer pairs.
39
+ *
40
+ * @param items - Array of question/answer objects
41
+ * @returns A JSON-LD object suitable for embedding in a `<script type="application/ld+json">` tag
42
+ * @example
43
+ * const jsonLd = buildFaqPageJsonLd([
44
+ * { question: 'What is Mars?', answer: 'A SaaS scaffold tool.' },
45
+ * ]);
46
+ */
47
+ export declare function buildFaqPageJsonLd(items: JsonLdFaqItem[]): Record<string, unknown>;
48
+ /**
49
+ * Creates a set of JSON-LD structured data builders scoped to a site's identity.
50
+ * Supports Organization, WebSite, SiteNavigation, BreadcrumbList, and Article schemas.
51
+ *
52
+ * @param config - Site identity with name, URL, description, and optional support email
53
+ * @returns Builder functions for each supported Schema.org type
54
+ * @example
55
+ * const seo = createSeoBuilders({ name: 'Acme', url: 'https://acme.com', description: 'SaaS' });
56
+ * const orgLd = seo.buildOrganizationJsonLd();
57
+ */
58
+ /**
59
+ * Creates a set of Schema.org JSON-LD builders pre-configured with site metadata.
60
+ * Returns builders for Organization, WebSite, SiteNavigation, BreadcrumbList, and Article.
61
+ *
62
+ * @param config - Site metadata including name, URL, description, and optional support email
63
+ * @returns An object with JSON-LD builder functions
64
+ * @example
65
+ * const seo = createSeoBuilders({ name: 'Acme', url: 'https://acme.com', description: 'SaaS app' });
66
+ * const orgLd = seo.buildOrganizationJsonLd();
67
+ */
68
+ export declare function createSeoBuilders(config: SeoConfig): {
69
+ buildOrganizationJsonLd: () => Record<string, unknown>;
70
+ buildWebSiteJsonLd: () => Record<string, unknown>;
71
+ buildSiteNavigationJsonLd: (items: SiteNavItem[]) => Record<string, unknown>;
72
+ buildBreadcrumbListJsonLd: (items: BreadcrumbItem[]) => Record<string, unknown>;
73
+ buildArticleJsonLd: (article: ArticleJsonLdInput) => Record<string, unknown>;
74
+ };
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAalF;AAED;;;;;;;;;GASG;AACH;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS;mCAIb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;8BAU5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;uCASZ,WAAW,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;uCAavC,cAAc,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;kCAa/C,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAkClF"}
@@ -0,0 +1 @@
1
+ export { buildFaqPageJsonLd, createSeoBuilders, toPlainTextFromFaqBlocks } from '../chunk-ABBUHT5Z.js';
@@ -0,0 +1,50 @@
1
+ import 'server-only';
2
+ export interface UploadParams {
3
+ filename: string;
4
+ contentType: string;
5
+ data: Buffer | ReadableStream | Blob;
6
+ access?: 'public' | 'private';
7
+ maxSizeBytes?: number;
8
+ }
9
+ export interface UploadResult {
10
+ url: string;
11
+ pathname: string;
12
+ contentType: string;
13
+ size: number;
14
+ }
15
+ export interface StorageProvider {
16
+ upload(params: UploadParams): Promise<UploadResult>;
17
+ delete(url: string): Promise<void>;
18
+ getSignedUrl?(url: string, expiresIn?: number): Promise<string>;
19
+ }
20
+ export interface StorageServiceConfig {
21
+ provider: string;
22
+ maxFileSizeBytes?: number;
23
+ }
24
+ /**
25
+ * Creates a file storage service backed by the configured provider
26
+ * (Vercel Blob, S3, or local filesystem). Enforces file size limits and
27
+ * sanitizes filenames across all providers.
28
+ *
29
+ * @param config - Configuration with provider name and optional global max file size
30
+ * @returns An object with upload, delete, and getSignedUrl methods
31
+ * @example
32
+ * const storage = createStorageService({ provider: 'vercel', maxFileSizeBytes: 10_000_000 });
33
+ * const result = await storage.upload({ filename: 'photo.jpg', contentType: 'image/jpeg', data: buffer });
34
+ */
35
+ /**
36
+ * Creates a storage service backed by the configured provider (Vercel Blob, S3, or local filesystem).
37
+ * Enforces an optional global max file size from the config.
38
+ *
39
+ * @param config - Configuration with provider name and optional max file size in bytes
40
+ * @returns An object with upload, delete, and getSignedUrl methods
41
+ * @example
42
+ * const storage = createStorageService({ provider: 'vercel', maxFileSizeBytes: 10_000_000 });
43
+ * const result = await storage.upload({ filename: 'avatar.png', contentType: 'image/png', data: buffer });
44
+ */
45
+ export declare function createStorageService(config: StorageServiceConfig): {
46
+ upload: (params: UploadParams) => Promise<UploadResult>;
47
+ delete: (url: string) => Promise<void>;
48
+ getSignedUrl: (url: string, expiresIn?: number) => Promise<string>;
49
+ };
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAGrB,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwOD;;;;;;;;;;GAUG;AACH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB;qBAIjC,YAAY,KAAG,OAAO,CAAC,YAAY,CAAC;kBAQnC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;wBAIpB,MAAM,cAAc,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;EAQ9E"}
@@ -0,0 +1,211 @@
1
+ import { importOptional } from '../chunk-CTYAVMOF.js';
2
+ import 'server-only';
3
+
4
+ function sanitizeFilename(filename) {
5
+ return filename.replace(/[^\w.\-]/g, "_").replace(/\.{2,}/g, ".").slice(0, 255);
6
+ }
7
+ function getDataSize(data) {
8
+ if (Buffer.isBuffer(data)) return data.byteLength;
9
+ if (data instanceof Blob) return data.size;
10
+ return null;
11
+ }
12
+ function validateFileSize(data, maxSizeBytes) {
13
+ if (!maxSizeBytes) return;
14
+ const size = getDataSize(data);
15
+ if (size !== null && size > maxSizeBytes) {
16
+ throw new Error(
17
+ `File size ${size} bytes exceeds maximum allowed size of ${maxSizeBytes} bytes`
18
+ );
19
+ }
20
+ }
21
+ function createVercelBlobProvider() {
22
+ return {
23
+ async upload(params) {
24
+ const { put } = await importOptional("@vercel/blob");
25
+ validateFileSize(params.data, params.maxSizeBytes);
26
+ const safe = sanitizeFilename(params.filename);
27
+ const blob = await put(safe, params.data, {
28
+ access: "public",
29
+ contentType: params.contentType,
30
+ addRandomSuffix: true
31
+ });
32
+ return {
33
+ url: blob.url,
34
+ pathname: blob.pathname,
35
+ contentType: params.contentType,
36
+ size: getDataSize(params.data) ?? 0
37
+ };
38
+ },
39
+ async delete(url) {
40
+ const { del } = await importOptional("@vercel/blob");
41
+ await del(url);
42
+ }
43
+ };
44
+ }
45
+ function createS3Provider() {
46
+ function getS3Config() {
47
+ const region = process.env.AWS_REGION;
48
+ const bucket = process.env.S3_BUCKET_NAME;
49
+ const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
50
+ const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
51
+ if (!region) throw new Error("AWS_REGION is not set");
52
+ if (!bucket) throw new Error("S3_BUCKET_NAME is not set");
53
+ if (!accessKeyId) throw new Error("AWS_ACCESS_KEY_ID is not set");
54
+ if (!secretAccessKey) throw new Error("AWS_SECRET_ACCESS_KEY is not set");
55
+ return { region, bucket, accessKeyId, secretAccessKey };
56
+ }
57
+ async function getClient() {
58
+ const { S3Client } = await importOptional("@aws-sdk/client-s3");
59
+ const config = getS3Config();
60
+ return {
61
+ client: new S3Client({
62
+ region: config.region,
63
+ credentials: {
64
+ accessKeyId: config.accessKeyId,
65
+ secretAccessKey: config.secretAccessKey
66
+ }
67
+ }),
68
+ bucket: config.bucket
69
+ };
70
+ }
71
+ return {
72
+ async upload(params) {
73
+ const { PutObjectCommand } = await importOptional("@aws-sdk/client-s3");
74
+ validateFileSize(params.data, params.maxSizeBytes);
75
+ const safe = sanitizeFilename(params.filename);
76
+ const key = `uploads/${Date.now()}-${safe}`;
77
+ const { client, bucket } = await getClient();
78
+ let body;
79
+ if (Buffer.isBuffer(params.data)) {
80
+ body = params.data;
81
+ } else if (params.data instanceof Blob) {
82
+ body = params.data;
83
+ } else {
84
+ const chunks = [];
85
+ const reader = params.data.getReader();
86
+ let done = false;
87
+ while (!done) {
88
+ const result = await reader.read();
89
+ done = result.done;
90
+ if (result.value) chunks.push(result.value);
91
+ }
92
+ body = Buffer.concat(chunks);
93
+ }
94
+ const command = new PutObjectCommand({
95
+ Bucket: bucket,
96
+ Key: key,
97
+ Body: body,
98
+ ContentType: params.contentType,
99
+ ACL: params.access === "public" ? "public-read" : "private"
100
+ });
101
+ await client.send(command);
102
+ const config = getS3Config();
103
+ const url = `https://${bucket}.s3.${config.region}.amazonaws.com/${key}`;
104
+ return {
105
+ url,
106
+ pathname: key,
107
+ contentType: params.contentType,
108
+ size: Buffer.isBuffer(body) ? body.byteLength : body.size
109
+ };
110
+ },
111
+ async delete(url) {
112
+ const { DeleteObjectCommand } = await importOptional("@aws-sdk/client-s3");
113
+ const { client, bucket } = await getClient();
114
+ const urlObj = new URL(url);
115
+ const key = urlObj.pathname.slice(1);
116
+ const command = new DeleteObjectCommand({
117
+ Bucket: bucket,
118
+ Key: key
119
+ });
120
+ await client.send(command);
121
+ },
122
+ async getSignedUrl(url, expiresIn = 3600) {
123
+ const { GetObjectCommand } = await importOptional("@aws-sdk/client-s3");
124
+ const { getSignedUrl: s3GetSignedUrl } = await importOptional("@aws-sdk/s3-request-presigner");
125
+ const { client, bucket } = await getClient();
126
+ const urlObj = new URL(url);
127
+ const key = urlObj.pathname.slice(1);
128
+ const command = new GetObjectCommand({
129
+ Bucket: bucket,
130
+ Key: key
131
+ });
132
+ return s3GetSignedUrl(client, command, { expiresIn });
133
+ }
134
+ };
135
+ }
136
+ function createLocalProvider() {
137
+ return {
138
+ async upload(params) {
139
+ const { writeFile, mkdir } = await import('fs/promises');
140
+ const { join } = await import('path');
141
+ validateFileSize(params.data, params.maxSizeBytes);
142
+ const safe = sanitizeFilename(params.filename);
143
+ const timestamped = `${Date.now()}-${safe}`;
144
+ const uploadsDir = join(process.cwd(), "public", "uploads");
145
+ await mkdir(uploadsDir, { recursive: true });
146
+ const filePath = join(uploadsDir, timestamped);
147
+ let buffer;
148
+ if (Buffer.isBuffer(params.data)) {
149
+ buffer = params.data;
150
+ } else if (params.data instanceof Blob) {
151
+ buffer = Buffer.from(await params.data.arrayBuffer());
152
+ } else {
153
+ const chunks = [];
154
+ const reader = params.data.getReader();
155
+ let done = false;
156
+ while (!done) {
157
+ const result = await reader.read();
158
+ done = result.done;
159
+ if (result.value) chunks.push(result.value);
160
+ }
161
+ buffer = Buffer.concat(chunks);
162
+ }
163
+ await writeFile(filePath, buffer);
164
+ return {
165
+ url: `/uploads/${timestamped}`,
166
+ pathname: `uploads/${timestamped}`,
167
+ contentType: params.contentType,
168
+ size: buffer.byteLength
169
+ };
170
+ },
171
+ async delete(url) {
172
+ const { unlink } = await import('fs/promises');
173
+ const { join } = await import('path');
174
+ const relativePath = url.startsWith("/") ? url.slice(1) : url;
175
+ const filePath = join(process.cwd(), "public", relativePath);
176
+ try {
177
+ await unlink(filePath);
178
+ } catch (error) {
179
+ if (error.code !== "ENOENT") throw error;
180
+ }
181
+ }
182
+ };
183
+ }
184
+ var providerFactories = {
185
+ vercel: createVercelBlobProvider,
186
+ s3: createS3Provider,
187
+ local: createLocalProvider
188
+ };
189
+ function createStorageService(config) {
190
+ const factory = providerFactories[config.provider] ?? providerFactories.local;
191
+ const provider = factory();
192
+ async function upload(params) {
193
+ const effectiveParams = {
194
+ ...params,
195
+ maxSizeBytes: params.maxSizeBytes ?? config.maxFileSizeBytes
196
+ };
197
+ return provider.upload(effectiveParams);
198
+ }
199
+ async function deleteFile(url) {
200
+ return provider.delete(url);
201
+ }
202
+ async function getSignedUrl(url, expiresIn) {
203
+ if (!provider.getSignedUrl) {
204
+ return url;
205
+ }
206
+ return provider.getSignedUrl(url, expiresIn);
207
+ }
208
+ return { upload, delete: deleteFile, getSignedUrl };
209
+ }
210
+
211
+ export { createStorageService };
@@ -0,0 +1,38 @@
1
+ interface MockUserOverrides {
2
+ id?: string;
3
+ email?: string;
4
+ name?: string;
5
+ role?: string;
6
+ emailVerified?: boolean;
7
+ image?: string | null;
8
+ }
9
+ export declare function createMockUser(overrides?: MockUserOverrides): {
10
+ id: string;
11
+ email: string;
12
+ name: string;
13
+ password: string;
14
+ role: string;
15
+ emailVerified: Date | null;
16
+ image: string | null;
17
+ failedLoginAttempts: number;
18
+ lastFailedLogin: null;
19
+ lockedUntil: null;
20
+ termsAcceptedAt: Date;
21
+ privacyAcceptedAt: Date;
22
+ marketingOptIn: boolean;
23
+ marketingOptInAt: null;
24
+ createdAt: Date;
25
+ updatedAt: Date;
26
+ };
27
+ interface MockSessionOverrides {
28
+ userId?: string;
29
+ role?: string;
30
+ }
31
+ export declare function createMockSession(overrides?: MockSessionOverrides): {
32
+ userId: string;
33
+ role: string;
34
+ email: string;
35
+ name: string;
36
+ };
37
+ export {};
38
+ //# sourceMappingURL=factories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../src/test-utils/factories.ts"],"names":[],"mappings":"AAAA,UAAU,iBAAiB;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,SAAS,GAAE,iBAAsB;;;;;;;;;;;;;;;;;EAmB/D;AAED,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,oBAAyB;;;;;EAOrE"}
@@ -0,0 +1,6 @@
1
+ export { renderWithProviders } from './render';
2
+ export { createMockUser, createMockSession } from './factories';
3
+ export { mockPrisma, resetMockPrisma } from './mock-prisma';
4
+ export { mockAuth, mockAuthAsAdmin, mockAuthAsUser } from './mock-auth';
5
+ export { createTestRequest, createTestRequestWithBody } from './request-helpers';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,117 @@
1
+ import { render } from '@testing-library/react';
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
+ import { vi } from 'vitest';
4
+ import { NextRequest } from 'next/server';
5
+
6
+ // src/test-utils/render.tsx
7
+ function AllProviders({ children }) {
8
+ return /* @__PURE__ */ jsx(Fragment, { children });
9
+ }
10
+ function renderWithProviders(ui, options) {
11
+ return render(ui, { wrapper: AllProviders, ...options });
12
+ }
13
+
14
+ // src/test-utils/factories.ts
15
+ function createMockUser(overrides = {}) {
16
+ return {
17
+ id: overrides.id ?? "user-test-id",
18
+ email: overrides.email ?? "test@example.com",
19
+ name: overrides.name ?? "Test User",
20
+ password: "$2a$10$mockhashedpassword",
21
+ role: overrides.role ?? "user",
22
+ emailVerified: overrides.emailVerified ? /* @__PURE__ */ new Date() : null,
23
+ image: overrides.image ?? null,
24
+ failedLoginAttempts: 0,
25
+ lastFailedLogin: null,
26
+ lockedUntil: null,
27
+ termsAcceptedAt: /* @__PURE__ */ new Date("2025-01-01"),
28
+ privacyAcceptedAt: /* @__PURE__ */ new Date("2025-01-01"),
29
+ marketingOptIn: false,
30
+ marketingOptInAt: null,
31
+ createdAt: /* @__PURE__ */ new Date("2025-01-01T00:00:00.000Z"),
32
+ updatedAt: /* @__PURE__ */ new Date("2025-01-01T00:00:00.000Z")
33
+ };
34
+ }
35
+ function createMockSession(overrides = {}) {
36
+ return {
37
+ userId: overrides.userId ?? "user-test-id",
38
+ role: overrides.role ?? "user",
39
+ email: "test@example.com",
40
+ name: "Test User"
41
+ };
42
+ }
43
+ function createMockModel() {
44
+ return {
45
+ findUnique: vi.fn(),
46
+ findFirst: vi.fn(),
47
+ findMany: vi.fn(),
48
+ create: vi.fn(),
49
+ update: vi.fn(),
50
+ delete: vi.fn(),
51
+ deleteMany: vi.fn(),
52
+ count: vi.fn(),
53
+ aggregate: vi.fn(),
54
+ upsert: vi.fn()
55
+ };
56
+ }
57
+ var mockPrisma = {
58
+ user: createMockModel(),
59
+ account: createMockModel(),
60
+ session: createMockModel(),
61
+ verificationToken: createMockModel(),
62
+ $transaction: vi.fn((fn) => fn(mockPrisma)),
63
+ $connect: vi.fn(),
64
+ $disconnect: vi.fn()
65
+ };
66
+ function resetMockPrisma() {
67
+ Object.values(mockPrisma).forEach((model) => {
68
+ if (typeof model === "object" && model !== null) {
69
+ Object.values(model).forEach((method) => {
70
+ if (typeof method === "function" && "mockReset" in method) {
71
+ method.mockReset();
72
+ }
73
+ });
74
+ }
75
+ });
76
+ }
77
+ vi.mock("@mars-stack/core/database", () => ({
78
+ prisma: mockPrisma
79
+ }));
80
+ function mockAuth(options = {}) {
81
+ const session = createMockSession({
82
+ userId: options.userId,
83
+ role: options.role
84
+ });
85
+ vi.doMock("@mars-stack/core/auth/session", () => ({
86
+ getSession: vi.fn().mockResolvedValue(session),
87
+ verifySession: vi.fn().mockResolvedValue(session)
88
+ }));
89
+ return session;
90
+ }
91
+ function mockAuthAsUser(userId = "user-test-id") {
92
+ return mockAuth({ userId, role: "user" });
93
+ }
94
+ function mockAuthAsAdmin(userId = "admin-test-id") {
95
+ return mockAuth({ userId, role: "admin" });
96
+ }
97
+ function createTestRequest(method, path) {
98
+ return new NextRequest(new URL(path, "http://localhost:3000"), {
99
+ method,
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ "x-csrf-token": "test-csrf-token"
103
+ }
104
+ });
105
+ }
106
+ function createTestRequestWithBody(method, path, body) {
107
+ return new NextRequest(new URL(path, "http://localhost:3000"), {
108
+ method,
109
+ headers: {
110
+ "Content-Type": "application/json",
111
+ "x-csrf-token": "test-csrf-token"
112
+ },
113
+ body: JSON.stringify(body)
114
+ });
115
+ }
116
+
117
+ export { createMockSession, createMockUser, createTestRequest, createTestRequestWithBody, mockAuth, mockAuthAsAdmin, mockAuthAsUser, mockPrisma, renderWithProviders, resetMockPrisma };
@@ -0,0 +1,25 @@
1
+ interface MockAuthOptions {
2
+ userId?: string;
3
+ role?: string;
4
+ email?: string;
5
+ }
6
+ export declare function mockAuth(options?: MockAuthOptions): {
7
+ userId: string;
8
+ role: string;
9
+ email: string;
10
+ name: string;
11
+ };
12
+ export declare function mockAuthAsUser(userId?: string): {
13
+ userId: string;
14
+ role: string;
15
+ email: string;
16
+ name: string;
17
+ };
18
+ export declare function mockAuthAsAdmin(userId?: string): {
19
+ userId: string;
20
+ role: string;
21
+ email: string;
22
+ name: string;
23
+ };
24
+ export {};
25
+ //# sourceMappingURL=mock-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-auth.d.ts","sourceRoot":"","sources":["../../src/test-utils/mock-auth.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB;;;;;EAYrD;AAED,wBAAgB,cAAc,CAAC,MAAM,SAAiB;;;;;EAErD;AAED,wBAAgB,eAAe,CAAC,MAAM,SAAkB;;;;;EAEvD"}
@@ -0,0 +1,55 @@
1
+ export declare const mockPrisma: {
2
+ user: {
3
+ findUnique: import("vitest").Mock<(...args: any[]) => any>;
4
+ findFirst: import("vitest").Mock<(...args: any[]) => any>;
5
+ findMany: import("vitest").Mock<(...args: any[]) => any>;
6
+ create: import("vitest").Mock<(...args: any[]) => any>;
7
+ update: import("vitest").Mock<(...args: any[]) => any>;
8
+ delete: import("vitest").Mock<(...args: any[]) => any>;
9
+ deleteMany: import("vitest").Mock<(...args: any[]) => any>;
10
+ count: import("vitest").Mock<(...args: any[]) => any>;
11
+ aggregate: import("vitest").Mock<(...args: any[]) => any>;
12
+ upsert: import("vitest").Mock<(...args: any[]) => any>;
13
+ };
14
+ account: {
15
+ findUnique: import("vitest").Mock<(...args: any[]) => any>;
16
+ findFirst: import("vitest").Mock<(...args: any[]) => any>;
17
+ findMany: import("vitest").Mock<(...args: any[]) => any>;
18
+ create: import("vitest").Mock<(...args: any[]) => any>;
19
+ update: import("vitest").Mock<(...args: any[]) => any>;
20
+ delete: import("vitest").Mock<(...args: any[]) => any>;
21
+ deleteMany: import("vitest").Mock<(...args: any[]) => any>;
22
+ count: import("vitest").Mock<(...args: any[]) => any>;
23
+ aggregate: import("vitest").Mock<(...args: any[]) => any>;
24
+ upsert: import("vitest").Mock<(...args: any[]) => any>;
25
+ };
26
+ session: {
27
+ findUnique: import("vitest").Mock<(...args: any[]) => any>;
28
+ findFirst: import("vitest").Mock<(...args: any[]) => any>;
29
+ findMany: import("vitest").Mock<(...args: any[]) => any>;
30
+ create: import("vitest").Mock<(...args: any[]) => any>;
31
+ update: import("vitest").Mock<(...args: any[]) => any>;
32
+ delete: import("vitest").Mock<(...args: any[]) => any>;
33
+ deleteMany: import("vitest").Mock<(...args: any[]) => any>;
34
+ count: import("vitest").Mock<(...args: any[]) => any>;
35
+ aggregate: import("vitest").Mock<(...args: any[]) => any>;
36
+ upsert: import("vitest").Mock<(...args: any[]) => any>;
37
+ };
38
+ verificationToken: {
39
+ findUnique: import("vitest").Mock<(...args: any[]) => any>;
40
+ findFirst: import("vitest").Mock<(...args: any[]) => any>;
41
+ findMany: import("vitest").Mock<(...args: any[]) => any>;
42
+ create: import("vitest").Mock<(...args: any[]) => any>;
43
+ update: import("vitest").Mock<(...args: any[]) => any>;
44
+ delete: import("vitest").Mock<(...args: any[]) => any>;
45
+ deleteMany: import("vitest").Mock<(...args: any[]) => any>;
46
+ count: import("vitest").Mock<(...args: any[]) => any>;
47
+ aggregate: import("vitest").Mock<(...args: any[]) => any>;
48
+ upsert: import("vitest").Mock<(...args: any[]) => any>;
49
+ };
50
+ $transaction: import("vitest").Mock<(fn: (tx: unknown) => unknown) => unknown>;
51
+ $connect: import("vitest").Mock<(...args: any[]) => any>;
52
+ $disconnect: import("vitest").Mock<(...args: any[]) => any>;
53
+ };
54
+ export declare function resetMockPrisma(): void;
55
+ //# sourceMappingURL=mock-prisma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-prisma.d.ts","sourceRoot":"","sources":["../../src/test-utils/mock-prisma.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CAKI,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO;;;CAGlD,CAAC;AAEF,wBAAgB,eAAe,SAU9B"}
@@ -0,0 +1,4 @@
1
+ import { type RenderOptions } from '@testing-library/react';
2
+ import type { ReactElement } from 'react';
3
+ export declare function renderWithProviders(ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>): import("@testing-library/react").RenderResult<typeof import("@testing-library/dom/types/queries"), HTMLElement, HTMLElement>;
4
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/test-utils/render.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAa,MAAM,OAAO,CAAC;AAUrD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,gIAE7F"}
@@ -0,0 +1,6 @@
1
+ import { NextRequest } from 'next/server';
2
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
3
+ export declare function createTestRequest(method: HttpMethod, path: string): NextRequest;
4
+ export declare function createTestRequestWithBody(method: HttpMethod, path: string, body: Record<string, unknown>): NextRequest;
5
+ export {};
6
+ //# sourceMappingURL=request-helpers.d.ts.map