@alfredmouelle/create-stack 0.1.1 → 0.2.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 (127) hide show
  1. package/README.md +41 -18
  2. package/_stack/apps/next-base/.turbo/turbo-typecheck.log +1 -0
  3. package/_stack/apps/next-base/.vscode/settings.json +35 -0
  4. package/_stack/apps/next-base/.zed/settings.json +45 -0
  5. package/_stack/apps/next-base/src/components/ui/spinner.tsx +1 -1
  6. package/_stack/apps/next-base/src/emails/components/components.tsx +2 -2
  7. package/_stack/apps/next-base/src/emails/components/context.tsx +1 -1
  8. package/_stack/apps/next-base/src/emails/components/theme.ts +6 -7
  9. package/_stack/apps/next-base/src/env.ts +2 -3
  10. package/_stack/apps/next-base/src/lib/date.ts +1 -1
  11. package/_stack/apps/next-base/src/server/auth/guards.ts +1 -1
  12. package/_stack/apps/next-base/src/server/better-auth/config.ts +1 -1
  13. package/_stack/apps/next-base/src/server/better-auth/server.ts +2 -2
  14. package/_stack/apps/next-base/src/server/db/schemas/index.ts +1 -1
  15. package/_stack/apps/next-base/src/server/db/seed.ts +2 -2
  16. package/_stack/apps/next-base/src/server/email/adapters/resend/index.ts +3 -3
  17. package/_stack/apps/next-base/src/server/email/core/address.ts +3 -3
  18. package/_stack/apps/next-base/src/server/email/core/port.ts +13 -20
  19. package/_stack/apps/next-base/src/server/email/core/render.ts +2 -2
  20. package/_stack/apps/next-base/src/server/email/factory.ts +7 -9
  21. package/_stack/apps/next-base/src/server/email/index.ts +1 -1
  22. package/_stack/apps/next-base/src/trpc/react.tsx +2 -2
  23. package/_stack/apps/next-base/src/trpc/server.ts +1 -1
  24. package/_stack/apps/tanstack-base/.turbo/turbo-typecheck.log +1 -0
  25. package/_stack/apps/tanstack-base/.vscode/settings.json +35 -0
  26. package/_stack/apps/tanstack-base/.zed/settings.json +45 -0
  27. package/_stack/apps/tanstack-base/src/components/form/text-field.tsx +1 -1
  28. package/_stack/apps/tanstack-base/src/components/ui/spinner.tsx +1 -1
  29. package/_stack/apps/tanstack-base/src/emails/components/components.tsx +2 -2
  30. package/_stack/apps/tanstack-base/src/emails/components/context.tsx +1 -1
  31. package/_stack/apps/tanstack-base/src/emails/components/theme.ts +6 -7
  32. package/_stack/apps/tanstack-base/src/env.ts +2 -6
  33. package/_stack/apps/tanstack-base/src/lib/date.ts +1 -1
  34. package/_stack/apps/tanstack-base/src/routes/__root.tsx +1 -1
  35. package/_stack/apps/tanstack-base/src/routes/_authed.tsx +1 -4
  36. package/_stack/apps/tanstack-base/src/routes/api/auth/$.ts +1 -1
  37. package/_stack/apps/tanstack-base/src/routes/api.trpc.$.tsx +1 -2
  38. package/_stack/apps/tanstack-base/src/server/better-auth/config.ts +1 -1
  39. package/_stack/apps/tanstack-base/src/server/better-auth/session.ts +6 -7
  40. package/_stack/apps/tanstack-base/src/server/db/schemas/index.ts +1 -1
  41. package/_stack/apps/tanstack-base/src/server/db/seed.ts +2 -2
  42. package/_stack/apps/tanstack-base/src/server/email/adapters/resend/index.ts +3 -3
  43. package/_stack/apps/tanstack-base/src/server/email/core/address.ts +3 -3
  44. package/_stack/apps/tanstack-base/src/server/email/core/port.ts +12 -22
  45. package/_stack/apps/tanstack-base/src/server/email/core/render.ts +1 -1
  46. package/_stack/apps/tanstack-base/src/server/email/factory.ts +7 -8
  47. package/_stack/apps/tanstack-base/src/server/email/index.ts +1 -1
  48. package/_stack/packages/analytics/package.json +26 -0
  49. package/_stack/packages/analytics/src/adapters/noop/index.ts +12 -0
  50. package/_stack/packages/analytics/src/adapters/plausible/config.ts +10 -0
  51. package/_stack/packages/analytics/src/adapters/plausible/index.ts +94 -0
  52. package/_stack/packages/analytics/src/adapters/posthog/config.ts +7 -0
  53. package/_stack/packages/analytics/src/adapters/posthog/index.ts +50 -0
  54. package/_stack/packages/analytics/src/core/port.ts +30 -0
  55. package/_stack/packages/analytics/src/index.ts +17 -0
  56. package/_stack/packages/cache/package.json +25 -0
  57. package/_stack/packages/cache/src/adapters/memory/index.ts +51 -0
  58. package/_stack/packages/cache/src/adapters/redis/config.ts +8 -0
  59. package/_stack/packages/cache/src/adapters/redis/index.ts +73 -0
  60. package/_stack/packages/cache/src/core/port.ts +29 -0
  61. package/_stack/packages/cache/src/core/wrap.ts +20 -0
  62. package/_stack/packages/cache/src/index.ts +12 -0
  63. package/_stack/packages/error-tracking/package.json +25 -0
  64. package/_stack/packages/error-tracking/src/adapters/console/index.ts +43 -0
  65. package/_stack/packages/error-tracking/src/adapters/sentry/config.ts +8 -0
  66. package/_stack/packages/error-tracking/src/adapters/sentry/index.ts +72 -0
  67. package/_stack/packages/error-tracking/src/core/port.ts +39 -0
  68. package/_stack/packages/error-tracking/src/index.ts +14 -0
  69. package/_stack/packages/http/package.json +20 -0
  70. package/_stack/packages/http/src/api.ts +373 -0
  71. package/_stack/packages/http/src/index.ts +14 -0
  72. package/_stack/packages/http/src/responses.ts +25 -0
  73. package/_stack/packages/http/src/types.ts +9 -0
  74. package/_stack/packages/jobs/package.json +27 -0
  75. package/_stack/packages/jobs/src/adapters/inngest/config.ts +8 -0
  76. package/_stack/packages/jobs/src/adapters/inngest/index.ts +93 -0
  77. package/_stack/packages/jobs/src/adapters/memory/index.ts +31 -0
  78. package/_stack/packages/jobs/src/adapters/trigger/config.ts +8 -0
  79. package/_stack/packages/jobs/src/adapters/trigger/index.ts +85 -0
  80. package/_stack/packages/jobs/src/core/port.ts +37 -0
  81. package/_stack/packages/jobs/src/index.ts +23 -0
  82. package/_stack/packages/logger/package.json +25 -0
  83. package/_stack/packages/logger/src/adapters/console/config.ts +7 -0
  84. package/_stack/packages/logger/src/adapters/console/index.ts +69 -0
  85. package/_stack/packages/logger/src/adapters/pino/index.ts +54 -0
  86. package/_stack/packages/logger/src/core/port.ts +21 -0
  87. package/_stack/packages/logger/src/index.ts +12 -0
  88. package/_stack/packages/mailer/src/adapters/brevo/index.ts +3 -3
  89. package/_stack/packages/mailer/src/adapters/resend/index.ts +3 -3
  90. package/_stack/packages/mailer/src/adapters/ses/config.ts +3 -3
  91. package/_stack/packages/mailer/src/adapters/ses/index.ts +4 -5
  92. package/_stack/packages/storage/package.json +27 -0
  93. package/_stack/packages/storage/src/adapters/gcs/config.ts +8 -0
  94. package/_stack/packages/storage/src/adapters/gcs/index.ts +111 -0
  95. package/_stack/packages/storage/src/adapters/local/config.ts +8 -0
  96. package/_stack/packages/storage/src/adapters/local/index.ts +78 -0
  97. package/_stack/packages/storage/src/adapters/r2/config.ts +8 -0
  98. package/_stack/packages/storage/src/adapters/r2/index.ts +39 -0
  99. package/_stack/packages/storage/src/adapters/s3/config.ts +11 -0
  100. package/_stack/packages/storage/src/adapters/s3/index.ts +143 -0
  101. package/_stack/packages/storage/src/core/port.ts +41 -0
  102. package/_stack/packages/storage/src/index.ts +21 -0
  103. package/index.mjs +89 -55
  104. package/lib/build.mjs +21 -11
  105. package/lib/capabilities.mjs +375 -0
  106. package/lib/env.mjs +26 -6
  107. package/lib/foundations.mjs +35 -0
  108. package/lib/identity.mjs +4 -5
  109. package/lib/mailer.mjs +9 -13
  110. package/lib/paths.mjs +15 -0
  111. package/lib/scaffold.mjs +12 -11
  112. package/lib/strip.mjs +9 -24
  113. package/lib/util.mjs +8 -9
  114. package/package.json +1 -1
  115. package/_stack/packages/mailer/capability.json +0 -28
  116. package/_stack/patterns/README.md +0 -58
  117. package/_stack/patterns/_baseline/env.ts +0 -31
  118. package/_stack/patterns/_baseline/tsconfig.json +0 -27
  119. package/_stack/patterns/better-auth/pattern.json +0 -73
  120. package/_stack/patterns/better-auth-next/pattern.json +0 -76
  121. package/_stack/patterns/data-table/pattern.json +0 -43
  122. package/_stack/patterns/drizzle/pattern.json +0 -61
  123. package/_stack/patterns/trpc/pattern.json +0 -61
  124. package/_stack/patterns/trpc-next/pattern.json +0 -64
  125. package/lib/manifests.mjs +0 -61
  126. /package/{_stack/patterns/_baseline → templates}/README-author.md +0 -0
  127. /package/{_stack/patterns/_baseline → templates}/biome.jsonc +0 -0
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@alfredmouelle/cache",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "scripts": {
10
+ "build": "tsdown",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "dependencies": {
16
+ "ioredis": "^5.11.1",
17
+ "valibot": "^1.4.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.10.2",
21
+ "tsdown": "^0.22.3",
22
+ "typescript": "^5.9.3",
23
+ "vitest": "^4.1.9"
24
+ }
25
+ }
@@ -0,0 +1,51 @@
1
+ import type { CachePort } from '../../core/port.js'
2
+ import { wrapValue } from '../../core/wrap.js'
3
+
4
+ interface Entry {
5
+ value: unknown
6
+ /** Epoch ms expiry; `undefined` = no expiry. */
7
+ expiresAt?: number
8
+ }
9
+
10
+ export interface MemoryAdapterOptions {
11
+ /** Seed store (mostly for tests). */
12
+ store?: Map<string, Entry>
13
+ }
14
+
15
+ /** In-process `Map` cache with lazy per-key expiry; no deps. Dev/tests. */
16
+ export function memoryAdapter(options: MemoryAdapterOptions = {}): CachePort {
17
+ const store = options.store ?? new Map<string, Entry>()
18
+
19
+ function read(key: string): Entry | null {
20
+ const entry = store.get(key)
21
+ if (entry === undefined) return null
22
+ if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {
23
+ store.delete(key)
24
+ return null
25
+ }
26
+ return entry
27
+ }
28
+
29
+ const port: CachePort = {
30
+ name: 'memory',
31
+ async get<T>(key: string) {
32
+ const entry = read(key)
33
+ return entry === null ? null : (entry.value as T)
34
+ },
35
+ async set<T>(key: string, value: T, ttlSeconds?: number) {
36
+ const expiresAt = ttlSeconds === undefined ? undefined : Date.now() + ttlSeconds * 1000
37
+ store.set(key, { value, expiresAt })
38
+ },
39
+ async delete(key: string) {
40
+ store.delete(key)
41
+ },
42
+ async has(key: string) {
43
+ return read(key) !== null
44
+ },
45
+ wrap<T>(key: string, factory: () => Promise<T>, ttlSeconds?: number) {
46
+ return wrapValue(port, key, factory, ttlSeconds)
47
+ },
48
+ }
49
+
50
+ return port
51
+ }
@@ -0,0 +1,8 @@
1
+ import * as v from 'valibot'
2
+
3
+ export const RedisConfigSchema = v.object({
4
+ url: v.optional(v.string()),
5
+ keyPrefix: v.optional(v.string()),
6
+ })
7
+
8
+ export type RedisConfig = v.InferOutput<typeof RedisConfigSchema>
@@ -0,0 +1,73 @@
1
+ import { Redis } from 'ioredis'
2
+ import * as v from 'valibot'
3
+ import { CacheError, type CachePort } from '../../core/port.js'
4
+ import { wrapValue } from '../../core/wrap.js'
5
+ import { RedisConfigSchema } from './config.js'
6
+
7
+ /** Minimal structural view of the Redis client (eases testing). */
8
+ export interface RedisLike {
9
+ get(key: string): Promise<string | null>
10
+ set(key: string, value: string, secondsToken?: 'EX', seconds?: number): Promise<unknown>
11
+ del(key: string): Promise<unknown>
12
+ exists(key: string): Promise<number>
13
+ }
14
+
15
+ export interface RedisAdapterOptions {
16
+ /** Inject a custom/mock client; defaults to a real `Redis`. */
17
+ client?: RedisLike
18
+ /** Connection URL when no `client` is injected. */
19
+ url?: string
20
+ /** Prepended to every key. */
21
+ keyPrefix?: string
22
+ }
23
+
24
+ export function redisAdapter(options: RedisAdapterOptions = {}): CachePort {
25
+ // Validate early: bad option fails at construction, not at use.
26
+ const config = v.parse(RedisConfigSchema, { url: options.url, keyPrefix: options.keyPrefix })
27
+ const defaultClient = () =>
28
+ (config.url ? new Redis(config.url) : new Redis()) as unknown as RedisLike
29
+ const client: RedisLike = options.client ?? defaultClient()
30
+ const prefix = config.keyPrefix ?? ''
31
+ const k = (key: string) => `${prefix}${key}`
32
+
33
+ const port: CachePort = {
34
+ name: 'redis',
35
+ async get<T>(key: string) {
36
+ try {
37
+ const raw = await client.get(k(key))
38
+ if (raw === null) return null
39
+ return JSON.parse(raw) as T
40
+ } catch (cause) {
41
+ throw new CacheError('Failed to read from Redis', { adapter: 'redis', cause })
42
+ }
43
+ },
44
+ async set<T>(key: string, value: T, ttlSeconds?: number) {
45
+ try {
46
+ const json = JSON.stringify(value)
47
+ if (ttlSeconds === undefined) await client.set(k(key), json)
48
+ else await client.set(k(key), json, 'EX', ttlSeconds)
49
+ } catch (cause) {
50
+ throw new CacheError('Failed to write to Redis', { adapter: 'redis', cause })
51
+ }
52
+ },
53
+ async delete(key: string) {
54
+ try {
55
+ await client.del(k(key))
56
+ } catch (cause) {
57
+ throw new CacheError('Failed to delete from Redis', { adapter: 'redis', cause })
58
+ }
59
+ },
60
+ async has(key: string) {
61
+ try {
62
+ return (await client.exists(k(key))) > 0
63
+ } catch (cause) {
64
+ throw new CacheError('Failed to query Redis', { adapter: 'redis', cause })
65
+ }
66
+ },
67
+ wrap<T>(key: string, factory: () => Promise<T>, ttlSeconds?: number) {
68
+ return wrapValue(port, key, factory, ttlSeconds)
69
+ },
70
+ }
71
+
72
+ return port
73
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * App-facing port; swap adapters at the composition root, never this interface.
3
+ * Values are JSON-serialized for remote stores, so stored values must be JSON-serializable.
4
+ */
5
+ export interface CachePort {
6
+ /** Backing adapter name (`redis`, `memory`, …). */
7
+ readonly name: string
8
+ /** Read a value, or `null` if absent/expired. */
9
+ get<T>(key: string): Promise<T | null>
10
+ /** Store a value, optionally expiring after `ttlSeconds`. */
11
+ set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>
12
+ /** Remove a key (no-op if absent). */
13
+ delete(key: string): Promise<void>
14
+ /** Whether a non-expired value exists. */
15
+ has(key: string): Promise<boolean>
16
+ /** Read-through: cached value, else `factory()` stored with `ttlSeconds`. */
17
+ wrap<T>(key: string, factory: () => Promise<T>, ttlSeconds?: number): Promise<T>
18
+ }
19
+
20
+ /** Normalized adapter error; callers never catch backend types. */
21
+ export class CacheError extends Error {
22
+ readonly adapter: string
23
+
24
+ constructor(message: string, options: { adapter: string; cause?: unknown }) {
25
+ super(message, { cause: options.cause })
26
+ this.name = 'CacheError'
27
+ this.adapter = options.adapter
28
+ }
29
+ }
@@ -0,0 +1,20 @@
1
+ /** Minimal read/write pair `wrap` needs; adapters pass their own `get`/`set`. */
2
+ export interface WrapStore {
3
+ get<T>(key: string): Promise<T | null>
4
+ set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>
5
+ }
6
+
7
+ /** Read-through: return cached value, else call `factory()`, store with `ttlSeconds`, return. */
8
+ export async function wrapValue<T>(
9
+ store: WrapStore,
10
+ key: string,
11
+ factory: () => Promise<T>,
12
+ ttlSeconds?: number,
13
+ ): Promise<T> {
14
+ const cached = await store.get<T>(key)
15
+ if (cached !== null) return cached
16
+
17
+ const value = await factory()
18
+ await store.set(key, value, ttlSeconds)
19
+ return value
20
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ type MemoryAdapterOptions,
3
+ memoryAdapter,
4
+ } from './adapters/memory/index.js'
5
+ export { type RedisConfig, RedisConfigSchema } from './adapters/redis/config.js'
6
+ export {
7
+ type RedisAdapterOptions,
8
+ type RedisLike,
9
+ redisAdapter,
10
+ } from './adapters/redis/index.js'
11
+ export type { CachePort } from './core/port.js'
12
+ export { CacheError } from './core/port.js'
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@alfredmouelle/error-tracking",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "scripts": {
10
+ "build": "tsdown",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "dependencies": {
16
+ "@sentry/node": "^10.59.0",
17
+ "valibot": "^1.4.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.10.2",
21
+ "tsdown": "^0.22.3",
22
+ "typescript": "^5.9.3",
23
+ "vitest": "^4.1.9"
24
+ }
25
+ }
@@ -0,0 +1,43 @@
1
+ import type {
2
+ Breadcrumb,
3
+ CaptureContext,
4
+ ErrorTrackingPort,
5
+ ErrorUser,
6
+ SeverityLevel,
7
+ } from '../../core/port.js'
8
+
9
+ export interface ConsoleAdapterOptions {
10
+ /** Max breadcrumbs kept in memory (oldest dropped). Default 20. */
11
+ maxBreadcrumbs?: number
12
+ }
13
+
14
+ /** Dev/test adapter logging to console; user + breadcrumb state kept in-memory (no transport). */
15
+ export function consoleAdapter(options: ConsoleAdapterOptions = {}): ErrorTrackingPort {
16
+ const maxBreadcrumbs = options.maxBreadcrumbs ?? 20
17
+ let user: ErrorUser | null = null
18
+ const breadcrumbs: Breadcrumb[] = []
19
+
20
+ function state() {
21
+ return { user, breadcrumbs }
22
+ }
23
+
24
+ return {
25
+ name: 'console',
26
+ captureException(error: unknown, context?: CaptureContext) {
27
+ console.error('[error-tracking] exception', error, { ...context, ...state() })
28
+ },
29
+ captureMessage(message: string, level: SeverityLevel = 'info') {
30
+ console.error('[error-tracking] message', { message, level, ...state() })
31
+ },
32
+ setUser(next: ErrorUser | null) {
33
+ user = next
34
+ },
35
+ addBreadcrumb(breadcrumb: Breadcrumb) {
36
+ breadcrumbs.push(breadcrumb)
37
+ if (breadcrumbs.length > maxBreadcrumbs) breadcrumbs.shift()
38
+ },
39
+ flush() {
40
+ return Promise.resolve(true)
41
+ },
42
+ }
43
+ }
@@ -0,0 +1,8 @@
1
+ import * as v from 'valibot'
2
+
3
+ export const SentryConfigSchema = v.object({
4
+ dsn: v.pipe(v.string(), v.minLength(1, 'SENTRY_DSN is required')),
5
+ environment: v.optional(v.string()),
6
+ })
7
+
8
+ export type SentryConfig = v.InferOutput<typeof SentryConfigSchema>
@@ -0,0 +1,72 @@
1
+ import * as Sentry from '@sentry/node'
2
+ import * as v from 'valibot'
3
+ import type {
4
+ Breadcrumb,
5
+ CaptureContext,
6
+ ErrorTrackingPort,
7
+ ErrorUser,
8
+ SeverityLevel,
9
+ } from '../../core/port.js'
10
+ import { SentryConfigSchema } from './config.js'
11
+
12
+ /** Minimal structural view of the Sentry namespace (eases testing). */
13
+ export interface SentryCaptureContext {
14
+ tags?: Record<string, string>
15
+ extra?: Record<string, unknown>
16
+ level?: SeverityLevel
17
+ }
18
+
19
+ export interface SentryLike {
20
+ init(options: { dsn: string; environment?: string }): void
21
+ captureException(error: unknown, context?: SentryCaptureContext): void
22
+ captureMessage(message: string, level?: SeverityLevel): void
23
+ setUser(user: ErrorUser | null): void
24
+ addBreadcrumb(breadcrumb: Breadcrumb): void
25
+ flush(timeoutMs?: number): Promise<boolean>
26
+ }
27
+
28
+ export interface SentryAdapterOptions {
29
+ dsn: string
30
+ environment?: string
31
+ /** Inject a custom/mock client; defaults to the real `@sentry/node`. */
32
+ client?: SentryLike
33
+ /** Skip the implicit `Sentry.init` (e.g. init happens elsewhere). */
34
+ init?: boolean
35
+ }
36
+
37
+ export function sentryAdapter(options: SentryAdapterOptions): ErrorTrackingPort {
38
+ // Validate early: missing DSN fails at construction, not at capture().
39
+ const config = v.parse(SentryConfigSchema, {
40
+ dsn: options.dsn,
41
+ environment: options.environment,
42
+ })
43
+ const client: SentryLike = options.client ?? (Sentry as unknown as SentryLike)
44
+
45
+ // Only init the real namespace; injected clients are assumed already wired.
46
+ if (options.init !== false && !options.client) {
47
+ client.init({ dsn: config.dsn, environment: config.environment })
48
+ }
49
+
50
+ return {
51
+ name: 'sentry',
52
+ captureException(error: unknown, context?: CaptureContext) {
53
+ client.captureException(error, {
54
+ tags: context?.tags,
55
+ extra: context?.extra,
56
+ level: context?.level,
57
+ })
58
+ },
59
+ captureMessage(message: string, level?: SeverityLevel) {
60
+ client.captureMessage(message, level)
61
+ },
62
+ setUser(user: ErrorUser | null) {
63
+ client.setUser(user)
64
+ },
65
+ addBreadcrumb(breadcrumb: Breadcrumb) {
66
+ client.addBreadcrumb(breadcrumb)
67
+ },
68
+ flush(timeoutMs?: number) {
69
+ return client.flush(timeoutMs)
70
+ },
71
+ }
72
+ }
@@ -0,0 +1,39 @@
1
+ /** Captured-event severity, matching common provider levels. */
2
+ export type SeverityLevel = 'fatal' | 'error' | 'warning' | 'info' | 'debug'
3
+
4
+ /** User an event is associated with. */
5
+ export interface ErrorUser {
6
+ id?: string
7
+ email?: string
8
+ username?: string
9
+ }
10
+
11
+ /** Trail of events leading up to an error, for debugging. */
12
+ export interface Breadcrumb {
13
+ message: string
14
+ category?: string
15
+ level?: SeverityLevel
16
+ data?: Record<string, unknown>
17
+ }
18
+
19
+ /** Extra context attached to a single capture. */
20
+ export interface CaptureContext {
21
+ tags?: Record<string, string>
22
+ extra?: Record<string, unknown>
23
+ level?: SeverityLevel
24
+ }
25
+
26
+ /** App-facing port; swap adapters at the composition root, never this interface. */
27
+ export interface ErrorTrackingPort {
28
+ readonly name: string
29
+ /** Report an error (or any thrown value) with optional context. */
30
+ captureException(error: unknown, context?: CaptureContext): void
31
+ /** Report a standalone message at severity (default `info`). */
32
+ captureMessage(message: string, level?: SeverityLevel): void
33
+ /** Associate subsequent events with a user; `null` clears. */
34
+ setUser(user: ErrorUser | null): void
35
+ /** Record a breadcrumb for subsequent events. */
36
+ addBreadcrumb(breadcrumb: Breadcrumb): void
37
+ /** Flush buffered events; resolves `true` if all sent in time. */
38
+ flush(timeoutMs?: number): Promise<boolean>
39
+ }
@@ -0,0 +1,14 @@
1
+ export { type ConsoleAdapterOptions, consoleAdapter } from './adapters/console/index.js'
2
+ export { type SentryConfig, SentryConfigSchema } from './adapters/sentry/config.js'
3
+ export {
4
+ type SentryAdapterOptions,
5
+ type SentryLike,
6
+ sentryAdapter,
7
+ } from './adapters/sentry/index.js'
8
+ export type {
9
+ Breadcrumb,
10
+ CaptureContext,
11
+ ErrorTrackingPort,
12
+ ErrorUser,
13
+ SeverityLevel,
14
+ } from './core/port.js'
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@alfredmouelle/http",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "scripts": {
10
+ "build": "tsdown",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "devDependencies": {
16
+ "tsdown": "^0.22.3",
17
+ "typescript": "^5.9.3",
18
+ "vitest": "^4.1.9"
19
+ }
20
+ }