ras-stack 2.0.2 → 3.0.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 (107) hide show
  1. package/README.md +15 -42
  2. package/dist/changesets/cli.d.ts +1 -0
  3. package/dist/changesets/cli.js +14 -0
  4. package/dist/changesets/cli.js.map +1 -0
  5. package/dist/{policy/changesets.js → changesets/index.js} +1 -1
  6. package/dist/changesets/index.js.map +1 -0
  7. package/dist/cli.d.ts +2 -6
  8. package/dist/cli.js +3 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/posthog/index.d.ts +0 -2
  11. package/dist/posthog/index.js +0 -1
  12. package/dist/posthog/index.js.map +1 -1
  13. package/package.json +12 -39
  14. package/config/oxlint/application.json +0 -16
  15. package/config/oxlint/domain.json +0 -29
  16. package/config/oxlint/layers.json +0 -116
  17. package/config/oxlint/tanstack.json +0 -4
  18. package/config/oxlint.json +0 -19
  19. package/config/typescript/base.json +0 -10
  20. package/config/typescript/browser.json +0 -8
  21. package/config/typescript/bundler.json +0 -9
  22. package/config/typescript/library.json +0 -11
  23. package/config/typescript/node-bundler.json +0 -9
  24. package/config/typescript/tanstack.json +0 -6
  25. package/dist/create/index.d.ts +0 -1
  26. package/dist/create/index.js +0 -83
  27. package/dist/create/index.js.map +0 -1
  28. package/dist/create/template/.env.example +0 -16
  29. package/dist/create/template/.oxfmtrc.json +0 -7
  30. package/dist/create/template/Dockerfile +0 -37
  31. package/dist/create/template/Dockerfile.standalone +0 -29
  32. package/dist/create/template/centrifugo.json +0 -14
  33. package/dist/create/template/dockerignore.template +0 -8
  34. package/dist/create/template/drizzle/0000_production_reference.sql +0 -114
  35. package/dist/create/template/drizzle/meta/_journal.json +0 -13
  36. package/dist/create/template/e2e/full-stack.spec.ts +0 -54
  37. package/dist/create/template/gitignore.template +0 -9
  38. package/dist/create/template/oxlint.json +0 -4
  39. package/dist/create/template/package.json +0 -60
  40. package/dist/create/template/playwright.config.ts +0 -7
  41. package/dist/create/template/pnpm-workspace.template.yaml +0 -13
  42. package/dist/create/template/ras-stack.assets.json +0 -4
  43. package/dist/create/template/scripts/containerRuntime.ts +0 -29
  44. package/dist/create/template/scripts/database.test.ts +0 -121
  45. package/dist/create/template/scripts/database.ts +0 -105
  46. package/dist/create/template/src/client/auth.ts +0 -3
  47. package/dist/create/template/src/client/queries.ts +0 -4
  48. package/dist/create/template/src/client/queryClient.ts +0 -1
  49. package/dist/create/template/src/client/useRealtime.ts +0 -20
  50. package/dist/create/template/src/posthog.ts +0 -21
  51. package/dist/create/template/src/routeTree.gen.ts +0 -230
  52. package/dist/create/template/src/router.tsx +0 -17
  53. package/dist/create/template/src/routes/__root.tsx +0 -40
  54. package/dist/create/template/src/routes/api/auth.$.ts +0 -8
  55. package/dist/create/template/src/routes/api/centrifugo.connect.ts +0 -29
  56. package/dist/create/template/src/routes/api/health.ts +0 -6
  57. package/dist/create/template/src/routes/api/live.ts +0 -5
  58. package/dist/create/template/src/routes/api/ready.ts +0 -30
  59. package/dist/create/template/src/routes/api/uploads.$id.ts +0 -40
  60. package/dist/create/template/src/routes/api/uploads.ts +0 -25
  61. package/dist/create/template/src/routes/index.tsx +0 -189
  62. package/dist/create/template/src/server/app.test.ts +0 -24
  63. package/dist/create/template/src/server/app.ts +0 -172
  64. package/dist/create/template/src/server/auth-flow.test.ts +0 -150
  65. package/dist/create/template/src/server/auth.ts +0 -56
  66. package/dist/create/template/src/server/environment.test.ts +0 -43
  67. package/dist/create/template/src/server/environment.ts +0 -67
  68. package/dist/create/template/src/server/fns.ts +0 -38
  69. package/dist/create/template/src/server/messages.test.ts +0 -38
  70. package/dist/create/template/src/server/messages.ts +0 -28
  71. package/dist/create/template/src/server/migration.test.ts +0 -34
  72. package/dist/create/template/src/server/outbox.test.ts +0 -130
  73. package/dist/create/template/src/server/outbox.ts +0 -110
  74. package/dist/create/template/src/server/posthog.test.ts +0 -34
  75. package/dist/create/template/src/server/rate-limit.test.ts +0 -32
  76. package/dist/create/template/src/server/rate-limit.ts +0 -29
  77. package/dist/create/template/src/server/rpc.ts +0 -16
  78. package/dist/create/template/src/server/schema.ts +0 -134
  79. package/dist/create/template/src/server/session.test.ts +0 -56
  80. package/dist/create/template/src/server/session.ts +0 -13
  81. package/dist/create/template/src/server/uploads.test.ts +0 -142
  82. package/dist/create/template/src/server/uploads.ts +0 -199
  83. package/dist/create/template/src/start.ts +0 -12
  84. package/dist/create/template/src/styles.css +0 -42
  85. package/dist/create/template/tsconfig.json +0 -8
  86. package/dist/create/template/vite.config.ts +0 -31
  87. package/dist/create/template/vitest.config.ts +0 -3
  88. package/dist/policy/changesets.js.map +0 -1
  89. package/dist/policy/cli.d.ts +0 -1
  90. package/dist/policy/cli.js +0 -23
  91. package/dist/policy/cli.js.map +0 -1
  92. package/dist/policy/index.d.ts +0 -14
  93. package/dist/policy/index.js +0 -132
  94. package/dist/policy/index.js.map +0 -1
  95. package/dist/policy/init-cli.d.ts +0 -12
  96. package/dist/policy/init-cli.js +0 -75
  97. package/dist/policy/init-cli.js.map +0 -1
  98. package/dist/policy/init.d.ts +0 -24
  99. package/dist/policy/init.js +0 -116
  100. package/dist/policy/init.js.map +0 -1
  101. package/dist/posthog/coverage.d.ts +0 -23
  102. package/dist/posthog/coverage.js +0 -30
  103. package/dist/posthog/coverage.js.map +0 -1
  104. package/dist/tanstack/query.d.ts +0 -3
  105. package/dist/tanstack/query.js +0 -8
  106. package/dist/tanstack/query.js.map +0 -1
  107. /package/dist/{policy/changesets.d.ts → changesets/index.d.ts} +0 -0
@@ -1,121 +0,0 @@
1
- import Database from 'better-sqlite3'
2
- import { mkdtemp, readdir, rm, writeFile } from 'node:fs/promises'
3
- import os from 'node:os'
4
- import path from 'node:path'
5
- import { afterEach, beforeEach, expect, it, vi } from 'vitest'
6
- import { runDatabaseCommand } from './database'
7
-
8
- let directory: string
9
- let source: string
10
-
11
- beforeEach(async () => {
12
- directory = await mkdtemp(path.join(os.tmpdir(), 'ras-stack-example-backup-'))
13
- source = path.join(directory, 'example.sqlite')
14
- writeValue(source, 'before')
15
- })
16
-
17
- afterEach(async () => {
18
- await rm(directory, { recursive: true, force: true })
19
- })
20
-
21
- it('backs up and restores a validated database', async () => {
22
- const backup = path.join(directory, 'backup.sqlite')
23
- await runDatabaseCommand('backup', backup, { DATA_DIR: directory })
24
- writeValue(source, 'after')
25
- await runDatabaseCommand('restore', backup, { DATA_DIR: directory })
26
- expect(readValue(source)).toBe('before')
27
- })
28
-
29
- it('rejects a corrupt restore candidate without changing the source', async () => {
30
- const corrupt = path.join(directory, 'corrupt.sqlite')
31
- await writeFile(corrupt, 'not sqlite')
32
- await expect(runDatabaseCommand('restore', corrupt, { DATA_DIR: directory })).rejects.toThrow('file is not a database')
33
- expect(readValue(source)).toBe('before')
34
- })
35
-
36
- it('restores the original file and removes temporary state when replacement fails', async () => {
37
- const backup = path.join(directory, 'backup.sqlite')
38
- await runDatabaseCommand('backup', backup, { DATA_DIR: directory })
39
- writeValue(source, 'current')
40
- const operations = await import('node:fs/promises')
41
- let renames = 0
42
- const rename = vi.fn(async (from: string, to: string) => {
43
- renames += 1
44
- if (renames === 2) throw new Error('replacement failed')
45
- await operations.rename(from, to)
46
- }) as typeof operations.rename
47
- await expect(runDatabaseCommand('restore', backup, { DATA_DIR: directory }, { ...operations, rename })).rejects.toThrow(
48
- 'replacement failed',
49
- )
50
- expect(readValue(source)).toBe('current')
51
- expect((await readdir(directory)).some((file) => file.includes('.restore-'))).toBe(false)
52
- })
53
-
54
- it('restores the original file even when temporary cleanup also fails', async () => {
55
- const backup = path.join(directory, 'backup.sqlite')
56
- await runDatabaseCommand('backup', backup, { DATA_DIR: directory })
57
- writeValue(source, 'current')
58
- const operations = await import('node:fs/promises')
59
- let renames = 0
60
- const rename = vi.fn(async (from: string, to: string) => {
61
- renames += 1
62
- if (renames === 2) throw new Error('replacement failed')
63
- await operations.rename(from, to)
64
- }) as typeof operations.rename
65
- const failure = await runDatabaseCommand(
66
- 'restore',
67
- backup,
68
- { DATA_DIR: directory },
69
- {
70
- ...operations,
71
- rename,
72
- rm: vi.fn().mockRejectedValue(new Error('cleanup failed')),
73
- },
74
- ).catch((error: unknown) => error)
75
- expect(failure).toBeInstanceOf(AggregateError)
76
- expect((failure as AggregateError).errors.map(String)).toEqual(['Error: replacement failed', 'Error: cleanup failed'])
77
- expect(readValue(source)).toBe('current')
78
- })
79
-
80
- it('reports rollback and cleanup failures after a replacement failure', async () => {
81
- const backup = path.join(directory, 'backup.sqlite')
82
- await runDatabaseCommand('backup', backup, { DATA_DIR: directory })
83
- const operations = await import('node:fs/promises')
84
- let renames = 0
85
- const rename = vi.fn(async (from: string, to: string) => {
86
- renames += 1
87
- if (renames === 2) throw new Error('replacement failed')
88
- if (renames === 3) throw new Error('rollback failed')
89
- await operations.rename(from, to)
90
- }) as typeof operations.rename
91
- const failure = await runDatabaseCommand(
92
- 'restore',
93
- backup,
94
- { DATA_DIR: directory },
95
- {
96
- ...operations,
97
- rename,
98
- rm: vi.fn().mockRejectedValue(new Error('cleanup failed')),
99
- },
100
- ).catch((error: unknown) => error)
101
- expect((failure as AggregateError).errors.map(String)).toEqual([
102
- 'Error: replacement failed',
103
- 'Error: rollback failed',
104
- 'Error: cleanup failed',
105
- ])
106
- expect(rename).toHaveBeenCalledTimes(3)
107
- })
108
-
109
- function writeValue(file: string, value: string) {
110
- const database = new Database(file)
111
- database.exec('DROP TABLE IF EXISTS proof; CREATE TABLE proof (value TEXT NOT NULL)')
112
- database.prepare('INSERT INTO proof (value) VALUES (?)').run(value)
113
- database.close()
114
- }
115
-
116
- function readValue(file: string) {
117
- const database = new Database(file, { readonly: true })
118
- const value = database.prepare('SELECT value FROM proof').pluck().get() as string
119
- database.close()
120
- return value
121
- }
@@ -1,105 +0,0 @@
1
- import Database from 'better-sqlite3'
2
- import { existsSync } from 'node:fs'
3
- import { copyFile, mkdir, rename, rm } from 'node:fs/promises'
4
- import path from 'node:path'
5
- import { fileURLToPath } from 'node:url'
6
-
7
- type FileOperations = { copyFile: typeof copyFile; mkdir: typeof mkdir; rename: typeof rename; rm: typeof rm }
8
- const defaultFileOperations: FileOperations = { copyFile, mkdir, rename, rm }
9
-
10
- export async function runDatabaseCommand(
11
- command: 'backup' | 'restore',
12
- suppliedPath: string | undefined,
13
- environment: NodeJS.ProcessEnv = process.env,
14
- fileOperations: FileOperations = defaultFileOperations,
15
- ) {
16
- const dataDirectory = path.resolve(environment.DATA_DIR ?? '.data/example-full-stack')
17
- const source = path.join(dataDirectory, 'example.sqlite')
18
- if (command === 'backup') {
19
- const backupDirectory = path.join(dataDirectory, 'backups')
20
- await fileOperations.mkdir(backupDirectory, { recursive: true })
21
- const destination = suppliedPath
22
- ? path.resolve(suppliedPath)
23
- : path.join(backupDirectory, `example-${new Date().toISOString().replaceAll(':', '-')}.sqlite`)
24
- const database = new Database(source, { readonly: true, fileMustExist: true })
25
- try {
26
- integrity(database, 'source')
27
- await database.backup(destination)
28
- const copy = new Database(destination, { readonly: true, fileMustExist: true })
29
- try {
30
- integrity(copy, 'backup')
31
- } finally {
32
- copy.close()
33
- }
34
- return destination
35
- } finally {
36
- database.close()
37
- }
38
- }
39
-
40
- if (!suppliedPath) throw new Error('restore requires a backup file')
41
- const candidate = path.resolve(suppliedPath)
42
- const backup = new Database(candidate, { readonly: true, fileMustExist: true })
43
- try {
44
- integrity(backup, 'backup')
45
- } finally {
46
- backup.close()
47
- }
48
- await fileOperations.mkdir(dataDirectory, { recursive: true })
49
- for (const sidecar of [`${source}-wal`, `${source}-shm`]) {
50
- if (existsSync(sidecar)) throw new Error(`Refusing to restore while SQLite sidecar exists: ${sidecar}`)
51
- }
52
- const temporary = `${source}.restore-${process.pid}`
53
- await fileOperations.copyFile(candidate, temporary)
54
- const restored = new Database(temporary, { readonly: true, fileMustExist: true })
55
- try {
56
- integrity(restored, 'restored')
57
- } finally {
58
- restored.close()
59
- }
60
- const previous = `${source}.before-restore-${Date.now()}`
61
- const hasPrevious = existsSync(source)
62
- if (hasPrevious) await fileOperations.rename(source, previous)
63
- try {
64
- await fileOperations.rename(temporary, source)
65
- } catch (error) {
66
- const failures = [error]
67
- if (hasPrevious) {
68
- try {
69
- await fileOperations.rename(previous, source)
70
- } catch (rollbackError) {
71
- failures.push(rollbackError)
72
- }
73
- }
74
- try {
75
- await fileOperations.rm(temporary, { force: true })
76
- } catch (cleanupError) {
77
- failures.push(cleanupError)
78
- }
79
- if (failures.length === 1) throw error
80
- const failure = new AggregateError(failures, 'Restore replacement failed and recovery was incomplete')
81
- failure.cause = error
82
- throw failure
83
- }
84
- return source
85
- }
86
-
87
- function integrity(database: Database.Database, label: string) {
88
- const result = database.pragma('quick_check', { simple: true })
89
- if (result !== 'ok') throw new Error(`${label} database failed quick_check: ${String(result)}`)
90
- }
91
-
92
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
93
- const [command, suppliedPath] = process.argv.slice(2)
94
- if (command !== 'backup' && command !== 'restore') {
95
- console.error('usage: database.mjs <backup [destination]|restore backup-file>')
96
- process.exitCode = 2
97
- } else {
98
- try {
99
- console.log(await runDatabaseCommand(command, suppliedPath))
100
- } catch (error) {
101
- console.error(error)
102
- process.exitCode = 1
103
- }
104
- }
105
- }
@@ -1,3 +0,0 @@
1
- import { createAuthClient } from 'better-auth/react'
2
-
3
- export const authClient = createAuthClient()
@@ -1,4 +0,0 @@
1
- import { queryOptions } from '@tanstack/react-query'
2
- import { snapshot } from '../server/fns'
3
-
4
- export const snapshotQuery = () => queryOptions({ queryKey: ['example', 'snapshot'], queryFn: () => snapshot() })
@@ -1 +0,0 @@
1
- export { createStackQueryClient as createQueryClient, queryErrorMessage } from 'ras-stack/tanstack/query'
@@ -1,20 +0,0 @@
1
- import { useQueryClient } from '@tanstack/react-query'
2
- import { createSameOriginRealtimeClient } from 'ras-stack/realtime/client'
3
- import { useConnectedRealtimeClient, useRealtimeSubscription } from 'ras-stack/realtime/react'
4
- import { useCallback } from 'react'
5
- import { snapshotQuery } from './queries'
6
-
7
- export function useRealtime(enabled: boolean) {
8
- const queryClient = useQueryClient()
9
- const create = useCallback(() => createSameOriginRealtimeClient({}), [])
10
- const client = useConnectedRealtimeClient(create, enabled)
11
- const configure = useCallback(
12
- (subscription: NonNullable<ReturnType<typeof useRealtimeSubscription>>) => {
13
- const refresh = () => void queryClient.invalidateQueries({ queryKey: snapshotQuery().queryKey })
14
- subscription.on('publication', refresh)
15
- return () => subscription.off('publication', refresh)
16
- },
17
- [queryClient],
18
- )
19
- useRealtimeSubscription({ client, channel: 'messages:all', enabled, configure })
20
- }
@@ -1,21 +0,0 @@
1
- import { definePostHogCoverage } from 'ras-stack/posthog'
2
-
3
- export const postHogCoverage = definePostHogCoverage({
4
- browser: {
5
- analytics: true,
6
- errorTracking: true,
7
- identity: true,
8
- logs: true,
9
- metrics: true,
10
- sessionReplay: { disabled: 'The example does not handle user data worth replaying' },
11
- featureFlags: { disabled: 'The example has no rollout-controlled behavior' },
12
- },
13
- server: {
14
- analytics: true,
15
- errorTracking: true,
16
- logs: true,
17
- metrics: true,
18
- tracing: true,
19
- },
20
- sourceMaps: { disabled: 'The example is not deployed as a user-facing application' },
21
- })
@@ -1,230 +0,0 @@
1
- /* eslint-disable */
2
-
3
- // @ts-nocheck
4
-
5
- // noinspection JSUnusedGlobalSymbols
6
-
7
- // This file was automatically generated by TanStack Router.
8
- // You should NOT make any changes in this file as it will be overwritten.
9
- // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
-
11
- import { Route as rootRouteImport } from './routes/__root'
12
- import { Route as IndexRouteImport } from './routes/index'
13
- import { Route as ApiHealthRouteImport } from './routes/api/health'
14
- import { Route as ApiLiveRouteImport } from './routes/api/live'
15
- import { Route as ApiReadyRouteImport } from './routes/api/ready'
16
- import { Route as ApiUploadsRouteImport } from './routes/api/uploads'
17
- import { Route as ApiAuthSplatRouteImport } from './routes/api/auth.$'
18
- import { Route as ApiCentrifugoConnectRouteImport } from './routes/api/centrifugo.connect'
19
- import { Route as ApiUploadsIdRouteImport } from './routes/api/uploads.$id'
20
-
21
- const IndexRoute = IndexRouteImport.update({
22
- id: '/',
23
- path: '/',
24
- getParentRoute: () => rootRouteImport,
25
- } as any)
26
- const ApiHealthRoute = ApiHealthRouteImport.update({
27
- id: '/api/health',
28
- path: '/api/health',
29
- getParentRoute: () => rootRouteImport,
30
- } as any)
31
- const ApiLiveRoute = ApiLiveRouteImport.update({
32
- id: '/api/live',
33
- path: '/api/live',
34
- getParentRoute: () => rootRouteImport,
35
- } as any)
36
- const ApiReadyRoute = ApiReadyRouteImport.update({
37
- id: '/api/ready',
38
- path: '/api/ready',
39
- getParentRoute: () => rootRouteImport,
40
- } as any)
41
- const ApiUploadsRoute = ApiUploadsRouteImport.update({
42
- id: '/api/uploads',
43
- path: '/api/uploads',
44
- getParentRoute: () => rootRouteImport,
45
- } as any)
46
- const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
47
- id: '/api/auth/$',
48
- path: '/api/auth/$',
49
- getParentRoute: () => rootRouteImport,
50
- } as any)
51
- const ApiCentrifugoConnectRoute = ApiCentrifugoConnectRouteImport.update({
52
- id: '/api/centrifugo/connect',
53
- path: '/api/centrifugo/connect',
54
- getParentRoute: () => rootRouteImport,
55
- } as any)
56
- const ApiUploadsIdRoute = ApiUploadsIdRouteImport.update({
57
- id: '/$id',
58
- path: '/$id',
59
- getParentRoute: () => ApiUploadsRoute,
60
- } as any)
61
-
62
- export interface FileRoutesByFullPath {
63
- '/': typeof IndexRoute
64
- '/api/health': typeof ApiHealthRoute
65
- '/api/live': typeof ApiLiveRoute
66
- '/api/ready': typeof ApiReadyRoute
67
- '/api/uploads': typeof ApiUploadsRouteWithChildren
68
- '/api/auth/$': typeof ApiAuthSplatRoute
69
- '/api/centrifugo/connect': typeof ApiCentrifugoConnectRoute
70
- '/api/uploads/$id': typeof ApiUploadsIdRoute
71
- }
72
- export interface FileRoutesByTo {
73
- '/': typeof IndexRoute
74
- '/api/health': typeof ApiHealthRoute
75
- '/api/live': typeof ApiLiveRoute
76
- '/api/ready': typeof ApiReadyRoute
77
- '/api/uploads': typeof ApiUploadsRouteWithChildren
78
- '/api/auth/$': typeof ApiAuthSplatRoute
79
- '/api/centrifugo/connect': typeof ApiCentrifugoConnectRoute
80
- '/api/uploads/$id': typeof ApiUploadsIdRoute
81
- }
82
- export interface FileRoutesById {
83
- __root__: typeof rootRouteImport
84
- '/': typeof IndexRoute
85
- '/api/health': typeof ApiHealthRoute
86
- '/api/live': typeof ApiLiveRoute
87
- '/api/ready': typeof ApiReadyRoute
88
- '/api/uploads': typeof ApiUploadsRouteWithChildren
89
- '/api/auth/$': typeof ApiAuthSplatRoute
90
- '/api/centrifugo/connect': typeof ApiCentrifugoConnectRoute
91
- '/api/uploads/$id': typeof ApiUploadsIdRoute
92
- }
93
- export interface FileRouteTypes {
94
- fileRoutesByFullPath: FileRoutesByFullPath
95
- fullPaths:
96
- | '/'
97
- | '/api/health'
98
- | '/api/live'
99
- | '/api/ready'
100
- | '/api/uploads'
101
- | '/api/auth/$'
102
- | '/api/centrifugo/connect'
103
- | '/api/uploads/$id'
104
- fileRoutesByTo: FileRoutesByTo
105
- to:
106
- | '/'
107
- | '/api/health'
108
- | '/api/live'
109
- | '/api/ready'
110
- | '/api/uploads'
111
- | '/api/auth/$'
112
- | '/api/centrifugo/connect'
113
- | '/api/uploads/$id'
114
- id:
115
- | '__root__'
116
- | '/'
117
- | '/api/health'
118
- | '/api/live'
119
- | '/api/ready'
120
- | '/api/uploads'
121
- | '/api/auth/$'
122
- | '/api/centrifugo/connect'
123
- | '/api/uploads/$id'
124
- fileRoutesById: FileRoutesById
125
- }
126
- export interface RootRouteChildren {
127
- IndexRoute: typeof IndexRoute
128
- ApiHealthRoute: typeof ApiHealthRoute
129
- ApiLiveRoute: typeof ApiLiveRoute
130
- ApiReadyRoute: typeof ApiReadyRoute
131
- ApiUploadsRoute: typeof ApiUploadsRouteWithChildren
132
- ApiAuthSplatRoute: typeof ApiAuthSplatRoute
133
- ApiCentrifugoConnectRoute: typeof ApiCentrifugoConnectRoute
134
- }
135
-
136
- declare module '@tanstack/react-router' {
137
- interface FileRoutesByPath {
138
- '/': {
139
- id: '/'
140
- path: '/'
141
- fullPath: '/'
142
- preLoaderRoute: typeof IndexRouteImport
143
- parentRoute: typeof rootRouteImport
144
- }
145
- '/api/health': {
146
- id: '/api/health'
147
- path: '/api/health'
148
- fullPath: '/api/health'
149
- preLoaderRoute: typeof ApiHealthRouteImport
150
- parentRoute: typeof rootRouteImport
151
- }
152
- '/api/live': {
153
- id: '/api/live'
154
- path: '/api/live'
155
- fullPath: '/api/live'
156
- preLoaderRoute: typeof ApiLiveRouteImport
157
- parentRoute: typeof rootRouteImport
158
- }
159
- '/api/ready': {
160
- id: '/api/ready'
161
- path: '/api/ready'
162
- fullPath: '/api/ready'
163
- preLoaderRoute: typeof ApiReadyRouteImport
164
- parentRoute: typeof rootRouteImport
165
- }
166
- '/api/uploads': {
167
- id: '/api/uploads'
168
- path: '/api/uploads'
169
- fullPath: '/api/uploads'
170
- preLoaderRoute: typeof ApiUploadsRouteImport
171
- parentRoute: typeof rootRouteImport
172
- }
173
- '/api/auth/$': {
174
- id: '/api/auth/$'
175
- path: '/api/auth/$'
176
- fullPath: '/api/auth/$'
177
- preLoaderRoute: typeof ApiAuthSplatRouteImport
178
- parentRoute: typeof rootRouteImport
179
- }
180
- '/api/centrifugo/connect': {
181
- id: '/api/centrifugo/connect'
182
- path: '/api/centrifugo/connect'
183
- fullPath: '/api/centrifugo/connect'
184
- preLoaderRoute: typeof ApiCentrifugoConnectRouteImport
185
- parentRoute: typeof rootRouteImport
186
- }
187
- '/api/uploads/$id': {
188
- id: '/api/uploads/$id'
189
- path: '/$id'
190
- fullPath: '/api/uploads/$id'
191
- preLoaderRoute: typeof ApiUploadsIdRouteImport
192
- parentRoute: typeof ApiUploadsRoute
193
- }
194
- }
195
- }
196
-
197
- interface ApiUploadsRouteChildren {
198
- ApiUploadsIdRoute: typeof ApiUploadsIdRoute
199
- }
200
-
201
- const ApiUploadsRouteChildren: ApiUploadsRouteChildren = {
202
- ApiUploadsIdRoute: ApiUploadsIdRoute,
203
- }
204
-
205
- const ApiUploadsRouteWithChildren = ApiUploadsRoute._addFileChildren(
206
- ApiUploadsRouteChildren,
207
- )
208
-
209
- const rootRouteChildren: RootRouteChildren = {
210
- IndexRoute: IndexRoute,
211
- ApiHealthRoute: ApiHealthRoute,
212
- ApiLiveRoute: ApiLiveRoute,
213
- ApiReadyRoute: ApiReadyRoute,
214
- ApiUploadsRoute: ApiUploadsRouteWithChildren,
215
- ApiAuthSplatRoute: ApiAuthSplatRoute,
216
- ApiCentrifugoConnectRoute: ApiCentrifugoConnectRoute,
217
- }
218
- export const routeTree = rootRouteImport
219
- ._addFileChildren(rootRouteChildren)
220
- ._addFileTypes<FileRouteTypes>()
221
-
222
- import type { getRouter } from './router.tsx'
223
- import type { startInstance } from './start.ts'
224
- declare module '@tanstack/react-start' {
225
- interface Register {
226
- ssr: true
227
- router: Awaited<ReturnType<typeof getRouter>>
228
- config: Awaited<ReturnType<typeof startInstance.getOptions>>
229
- }
230
- }
@@ -1,17 +0,0 @@
1
- import { createRouter } from '@tanstack/react-router'
2
- import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
3
- import { createQueryClient } from './client/queryClient'
4
- import { routeTree } from './routeTree.gen'
5
-
6
- export function getRouter() {
7
- const queryClient = createQueryClient()
8
- const router = createRouter({ routeTree, context: { queryClient }, defaultPreload: 'intent' })
9
- setupRouterSsrQueryIntegration({ router, queryClient })
10
- return router
11
- }
12
-
13
- declare module '@tanstack/react-router' {
14
- interface Register {
15
- router: ReturnType<typeof getRouter>
16
- }
17
- }
@@ -1,40 +0,0 @@
1
- import type { QueryClient } from '@tanstack/react-query'
2
- import { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/react-router'
3
- import { postHogEnvironment } from 'ras-stack/posthog'
4
- import { PostHogIntegration } from 'ras-stack/posthog/react'
5
- import styles from '../styles.css?url'
6
-
7
- const posthog = postHogEnvironment({
8
- projectToken: import.meta.env.VITE_POSTHOG_PROJECT_TOKEN,
9
- host: import.meta.env.VITE_POSTHOG_HOST,
10
- })
11
-
12
- const posthogService = { name: 'ras-stack-example-web', environment: import.meta.env.MODE }
13
-
14
- export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
15
- head: () => ({
16
- meta: [
17
- { charSet: 'utf-8' },
18
- { name: 'viewport', content: 'width=device-width, initial-scale=1' },
19
- { title: 'ras-stack full-stack example' },
20
- ],
21
- links: [{ rel: 'stylesheet', href: styles }],
22
- }),
23
- component: Root,
24
- })
25
-
26
- function Root() {
27
- return (
28
- <html lang="en">
29
- <head>
30
- <HeadContent />
31
- </head>
32
- <body>
33
- <PostHogIntegration environment={posthog} service={posthogService}>
34
- <Outlet />
35
- </PostHogIntegration>
36
- <Scripts />
37
- </body>
38
- </html>
39
- )
40
- }
@@ -1,8 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { app } from '../../server/app'
3
-
4
- const handler = ({ request }: { request: Request }) => app().auth.handler(request)
5
-
6
- export const Route = createFileRoute('/api/auth/$')({
7
- server: { handlers: { GET: handler, POST: handler } },
8
- })
@@ -1,29 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import crypto from 'node:crypto'
3
- import { requireTanStackMutationOrigin } from 'ras-stack/tanstack/server'
4
- import { currentUser } from '../../server/session'
5
-
6
- export const Route = createFileRoute('/api/centrifugo/connect')({
7
- server: {
8
- handlers: {
9
- POST: async ({ request }) => {
10
- if (!proxySecret(request)) return Response.json({ disconnect: { code: 4501, reason: 'unauthorized' } })
11
- try {
12
- requireTanStackMutationOrigin({ configured: [process.env.APP_URL], trustForwardedHeaders: true }, request)
13
- } catch {
14
- return Response.json({ disconnect: { code: 4501, reason: 'unauthorized' } })
15
- }
16
- const user = await currentUser(request)
17
- return user
18
- ? Response.json({ result: { user: user.id, info: { name: user.name } } })
19
- : Response.json({ disconnect: { code: 4501, reason: 'unauthorized' } })
20
- },
21
- },
22
- },
23
- })
24
-
25
- function proxySecret(request: Request) {
26
- const expected = Buffer.from(process.env.CENTRIFUGO_PROXY_SECRET ?? '')
27
- const received = Buffer.from(request.headers.get('x-proxy-secret') ?? '')
28
- return expected.length > 0 && expected.length === received.length && crypto.timingSafeEqual(expected, received)
29
- }
@@ -1,6 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { readinessHandler } from './ready'
3
-
4
- export const Route = createFileRoute('/api/health')({
5
- server: { handlers: { GET: readinessHandler } },
6
- })
@@ -1,5 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/api/live')({
4
- server: { handlers: { GET: () => Response.json({ status: 'ok' }, { headers: { 'Cache-Control': 'no-store' } }) } },
5
- })
@@ -1,30 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { sql } from 'drizzle-orm'
3
- import { infrastructureFailure } from 'ras-stack/server'
4
- import { tanStackHealthHandler } from 'ras-stack/tanstack/server'
5
- import { app } from '../../server/app'
6
- import { outboxStatus } from '../../server/outbox'
7
-
8
- export const readinessHandler = tanStackHealthHandler(
9
- async () => {
10
- try {
11
- app().database.get(sql`SELECT 1`)
12
- const status = outboxStatus(app().database)
13
- if (status.failed > 0) throw new Error(`Realtime outbox has ${status.failed} dead-lettered item(s)`)
14
- await app().email?.verify()
15
- } catch (error) {
16
- console.error({ event: 'example_readiness_failed', error })
17
- throw error
18
- }
19
- },
20
- {
21
- failure: (error) =>
22
- infrastructureFailure(error, { code: 'dependency_unavailable', message: 'required dependency unavailable', retryable: true }),
23
- },
24
- )
25
-
26
- export const Route = createFileRoute('/api/ready')({
27
- server: {
28
- handlers: { GET: readinessHandler },
29
- },
30
- })