ras-stack 2.0.2 → 3.0.1

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 (110) hide show
  1. package/README.md +19 -46
  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/email/index.d.ts +1 -2
  11. package/dist/email/index.js +2 -2
  12. package/dist/email/index.js.map +1 -1
  13. package/dist/posthog/index.d.ts +0 -2
  14. package/dist/posthog/index.js +0 -1
  15. package/dist/posthog/index.js.map +1 -1
  16. package/package.json +14 -41
  17. package/config/oxlint/application.json +0 -16
  18. package/config/oxlint/domain.json +0 -29
  19. package/config/oxlint/layers.json +0 -116
  20. package/config/oxlint/tanstack.json +0 -4
  21. package/config/oxlint.json +0 -19
  22. package/config/typescript/base.json +0 -10
  23. package/config/typescript/browser.json +0 -8
  24. package/config/typescript/bundler.json +0 -9
  25. package/config/typescript/library.json +0 -11
  26. package/config/typescript/node-bundler.json +0 -9
  27. package/config/typescript/tanstack.json +0 -6
  28. package/dist/create/index.d.ts +0 -1
  29. package/dist/create/index.js +0 -83
  30. package/dist/create/index.js.map +0 -1
  31. package/dist/create/template/.env.example +0 -16
  32. package/dist/create/template/.oxfmtrc.json +0 -7
  33. package/dist/create/template/Dockerfile +0 -37
  34. package/dist/create/template/Dockerfile.standalone +0 -29
  35. package/dist/create/template/centrifugo.json +0 -14
  36. package/dist/create/template/dockerignore.template +0 -8
  37. package/dist/create/template/drizzle/0000_production_reference.sql +0 -114
  38. package/dist/create/template/drizzle/meta/_journal.json +0 -13
  39. package/dist/create/template/e2e/full-stack.spec.ts +0 -54
  40. package/dist/create/template/gitignore.template +0 -9
  41. package/dist/create/template/oxlint.json +0 -4
  42. package/dist/create/template/package.json +0 -60
  43. package/dist/create/template/playwright.config.ts +0 -7
  44. package/dist/create/template/pnpm-workspace.template.yaml +0 -13
  45. package/dist/create/template/ras-stack.assets.json +0 -4
  46. package/dist/create/template/scripts/containerRuntime.ts +0 -29
  47. package/dist/create/template/scripts/database.test.ts +0 -121
  48. package/dist/create/template/scripts/database.ts +0 -105
  49. package/dist/create/template/src/client/auth.ts +0 -3
  50. package/dist/create/template/src/client/queries.ts +0 -4
  51. package/dist/create/template/src/client/queryClient.ts +0 -1
  52. package/dist/create/template/src/client/useRealtime.ts +0 -20
  53. package/dist/create/template/src/posthog.ts +0 -21
  54. package/dist/create/template/src/routeTree.gen.ts +0 -230
  55. package/dist/create/template/src/router.tsx +0 -17
  56. package/dist/create/template/src/routes/__root.tsx +0 -40
  57. package/dist/create/template/src/routes/api/auth.$.ts +0 -8
  58. package/dist/create/template/src/routes/api/centrifugo.connect.ts +0 -29
  59. package/dist/create/template/src/routes/api/health.ts +0 -6
  60. package/dist/create/template/src/routes/api/live.ts +0 -5
  61. package/dist/create/template/src/routes/api/ready.ts +0 -30
  62. package/dist/create/template/src/routes/api/uploads.$id.ts +0 -40
  63. package/dist/create/template/src/routes/api/uploads.ts +0 -25
  64. package/dist/create/template/src/routes/index.tsx +0 -189
  65. package/dist/create/template/src/server/app.test.ts +0 -24
  66. package/dist/create/template/src/server/app.ts +0 -172
  67. package/dist/create/template/src/server/auth-flow.test.ts +0 -150
  68. package/dist/create/template/src/server/auth.ts +0 -56
  69. package/dist/create/template/src/server/environment.test.ts +0 -43
  70. package/dist/create/template/src/server/environment.ts +0 -67
  71. package/dist/create/template/src/server/fns.ts +0 -38
  72. package/dist/create/template/src/server/messages.test.ts +0 -38
  73. package/dist/create/template/src/server/messages.ts +0 -28
  74. package/dist/create/template/src/server/migration.test.ts +0 -34
  75. package/dist/create/template/src/server/outbox.test.ts +0 -130
  76. package/dist/create/template/src/server/outbox.ts +0 -110
  77. package/dist/create/template/src/server/posthog.test.ts +0 -34
  78. package/dist/create/template/src/server/rate-limit.test.ts +0 -32
  79. package/dist/create/template/src/server/rate-limit.ts +0 -29
  80. package/dist/create/template/src/server/rpc.ts +0 -16
  81. package/dist/create/template/src/server/schema.ts +0 -134
  82. package/dist/create/template/src/server/session.test.ts +0 -56
  83. package/dist/create/template/src/server/session.ts +0 -13
  84. package/dist/create/template/src/server/uploads.test.ts +0 -142
  85. package/dist/create/template/src/server/uploads.ts +0 -199
  86. package/dist/create/template/src/start.ts +0 -12
  87. package/dist/create/template/src/styles.css +0 -42
  88. package/dist/create/template/tsconfig.json +0 -8
  89. package/dist/create/template/vite.config.ts +0 -31
  90. package/dist/create/template/vitest.config.ts +0 -3
  91. package/dist/policy/changesets.js.map +0 -1
  92. package/dist/policy/cli.d.ts +0 -1
  93. package/dist/policy/cli.js +0 -23
  94. package/dist/policy/cli.js.map +0 -1
  95. package/dist/policy/index.d.ts +0 -14
  96. package/dist/policy/index.js +0 -132
  97. package/dist/policy/index.js.map +0 -1
  98. package/dist/policy/init-cli.d.ts +0 -12
  99. package/dist/policy/init-cli.js +0 -75
  100. package/dist/policy/init-cli.js.map +0 -1
  101. package/dist/policy/init.d.ts +0 -24
  102. package/dist/policy/init.js +0 -116
  103. package/dist/policy/init.js.map +0 -1
  104. package/dist/posthog/coverage.d.ts +0 -23
  105. package/dist/posthog/coverage.js +0 -30
  106. package/dist/posthog/coverage.js.map +0 -1
  107. package/dist/tanstack/query.d.ts +0 -3
  108. package/dist/tanstack/query.js +0 -8
  109. package/dist/tanstack/query.js.map +0 -1
  110. /package/dist/{policy/changesets.d.ts → changesets/index.d.ts} +0 -0
@@ -1,19 +0,0 @@
1
- {
2
- "plugins": ["typescript", "unicorn", "oxc", "react", "jsx-a11y", "import", "vitest"],
3
- "categories": {
4
- "correctness": "error",
5
- "suspicious": "warn",
6
- "perf": "warn"
7
- },
8
- "rules": {
9
- "import/no-unassigned-import": "off",
10
- "react/react-in-jsx-scope": "off",
11
- "typescript/consistent-return": "off",
12
- "typescript/no-unsafe-type-assertion": "off",
13
- "unicorn/filename-case": "off",
14
- "vitest/require-mock-type-parameters": "off"
15
- },
16
- "env": {
17
- "builtin": true
18
- }
19
- }
@@ -1,10 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "noUncheckedIndexedAccess": true,
5
- "esModuleInterop": true,
6
- "skipLibCheck": true,
7
- "forceConsistentCasingInFileNames": true,
8
- "verbatimModuleSyntax": true
9
- }
10
- }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "./bundler.json",
3
- "compilerOptions": {
4
- "jsx": "react-jsx",
5
- "lib": ["ES2023", "DOM", "DOM.Iterable"],
6
- "noEmit": true
7
- }
8
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./base.json",
3
- "compilerOptions": {
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "target": "ES2023",
7
- "lib": ["ES2023"]
8
- }
9
- }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "./base.json",
3
- "compilerOptions": {
4
- "exactOptionalPropertyTypes": true,
5
- "module": "NodeNext",
6
- "moduleResolution": "NodeNext",
7
- "target": "ES2024",
8
- "lib": ["ES2024", "DOM", "DOM.Iterable"],
9
- "skipLibCheck": true
10
- }
11
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./bundler.json",
3
- "compilerOptions": {
4
- "target": "ES2024",
5
- "lib": ["ES2024"],
6
- "types": ["node"],
7
- "noEmit": true
8
- }
9
- }
@@ -1,6 +0,0 @@
1
- {
2
- "extends": "./browser.json",
3
- "compilerOptions": {
4
- "types": ["vite/client", "node"]
5
- }
6
- }
@@ -1 +0,0 @@
1
- export declare function runCreateCli(arguments_: string[]): Promise<void>;
@@ -1,83 +0,0 @@
1
- import { copyFile, cp, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
2
- import path from 'node:path';
3
- export async function runCreateCli(arguments_) {
4
- const dryRun = arguments_.includes('--dry-run');
5
- const positional = arguments_.filter((argument) => argument !== '--dry-run');
6
- if (positional.length !== 1) {
7
- console.error('usage: ras create <directory> [--dry-run]');
8
- process.exitCode = 2;
9
- return;
10
- }
11
- const destination = path.resolve(positional[0]);
12
- const source = await starterSource();
13
- await assertSource(source);
14
- await assertEmptyDestination(destination);
15
- if (dryRun) {
16
- console.log(destination);
17
- return;
18
- }
19
- await mkdir(destination, { recursive: true });
20
- await cp(source, destination, {
21
- recursive: true,
22
- filter: (entry) => !excluded(path.relative(source, entry)),
23
- });
24
- await materializeTemplate(destination, 'gitignore.template', '.gitignore');
25
- await materializeTemplate(destination, 'dockerignore.template', '.dockerignore');
26
- await materializeTemplate(destination, 'pnpm-workspace.template.yaml', 'pnpm-workspace.yaml');
27
- await copyFile(path.join(destination, 'Dockerfile.standalone'), path.join(destination, 'Dockerfile'));
28
- await rm(path.join(destination, 'Dockerfile.standalone'));
29
- const packageFile = path.join(destination, 'package.json');
30
- const manifest = JSON.parse(await readFile(packageFile, 'utf8'));
31
- const packageManifest = JSON.parse(await readFile(path.resolve(import.meta.dirname, '../../package.json'), 'utf8'));
32
- manifest.name = packageName(path.basename(destination));
33
- manifest.dependencies['ras-stack'] = `^${packageManifest.version}`;
34
- manifest.scripts.build = manifest.scripts.build.replaceAll('node ../../packages/ras-stack/dist/cli.js', 'ras');
35
- await writeFile(packageFile, `${JSON.stringify(manifest, null, 2)}\n`);
36
- console.log(destination);
37
- }
38
- async function starterSource() {
39
- const bundled = path.resolve(import.meta.dirname, 'template');
40
- try {
41
- await assertSource(bundled);
42
- return bundled;
43
- }
44
- catch (error) {
45
- if (error.code !== 'ENOENT')
46
- throw error;
47
- }
48
- const workspace = path.resolve(import.meta.dirname, '../../../../examples/full-stack');
49
- await assertSource(workspace);
50
- return workspace;
51
- }
52
- async function materializeTemplate(directory, template, output) {
53
- const source = path.join(directory, template);
54
- await copyFile(source, path.join(directory, output));
55
- await rm(source);
56
- }
57
- function excluded(relative) {
58
- const [root] = relative.split(path.sep);
59
- return ['.output', '.data', 'node_modules', 'test-results'].includes(root ?? '');
60
- }
61
- async function assertSource(source) {
62
- if (!(await stat(path.join(source, 'package.json'))).isFile())
63
- throw new Error('The packaged full-stack starter is missing');
64
- }
65
- async function assertEmptyDestination(destination) {
66
- try {
67
- const info = await stat(destination);
68
- if (!info.isDirectory() || (await readdir(destination)).length > 0)
69
- throw new Error(`Destination must be an empty directory: ${destination}`);
70
- }
71
- catch (error) {
72
- if (error.code !== 'ENOENT')
73
- throw error;
74
- }
75
- }
76
- function packageName(name) {
77
- const normalized = name
78
- .toLowerCase()
79
- .replaceAll(/[^a-z\d._-]/g, '-')
80
- .replaceAll(/^-+|-+$/g, '');
81
- return normalized || 'ras-stack-app';
82
- }
83
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyFile, cp, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\n\nexport async function runCreateCli(arguments_: string[]) {\n const dryRun = arguments_.includes('--dry-run')\n const positional = arguments_.filter((argument) => argument !== '--dry-run')\n if (positional.length !== 1) {\n console.error('usage: ras create <directory> [--dry-run]')\n process.exitCode = 2\n return\n }\n const destination = path.resolve(positional[0]!)\n const source = await starterSource()\n await assertSource(source)\n await assertEmptyDestination(destination)\n if (dryRun) {\n console.log(destination)\n return\n }\n await mkdir(destination, { recursive: true })\n await cp(source, destination, {\n recursive: true,\n filter: (entry) => !excluded(path.relative(source, entry)),\n })\n await materializeTemplate(destination, 'gitignore.template', '.gitignore')\n await materializeTemplate(destination, 'dockerignore.template', '.dockerignore')\n await materializeTemplate(destination, 'pnpm-workspace.template.yaml', 'pnpm-workspace.yaml')\n await copyFile(path.join(destination, 'Dockerfile.standalone'), path.join(destination, 'Dockerfile'))\n await rm(path.join(destination, 'Dockerfile.standalone'))\n const packageFile = path.join(destination, 'package.json')\n const manifest = JSON.parse(await readFile(packageFile, 'utf8')) as {\n name: string\n dependencies: Record<string, string>\n scripts: Record<string, string>\n }\n const packageManifest = JSON.parse(await readFile(path.resolve(import.meta.dirname, '../../package.json'), 'utf8')) as { version: string }\n manifest.name = packageName(path.basename(destination))\n manifest.dependencies['ras-stack'] = `^${packageManifest.version}`\n manifest.scripts.build = manifest.scripts.build!.replaceAll('node ../../packages/ras-stack/dist/cli.js', 'ras')\n await writeFile(packageFile, `${JSON.stringify(manifest, null, 2)}\\n`)\n console.log(destination)\n}\n\nasync function starterSource() {\n const bundled = path.resolve(import.meta.dirname, 'template')\n try {\n await assertSource(bundled)\n return bundled\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const workspace = path.resolve(import.meta.dirname, '../../../../examples/full-stack')\n await assertSource(workspace)\n return workspace\n}\n\nasync function materializeTemplate(directory: string, template: string, output: string) {\n const source = path.join(directory, template)\n await copyFile(source, path.join(directory, output))\n await rm(source)\n}\n\nfunction excluded(relative: string) {\n const [root] = relative.split(path.sep)\n return ['.output', '.data', 'node_modules', 'test-results'].includes(root ?? '')\n}\n\nasync function assertSource(source: string) {\n if (!(await stat(path.join(source, 'package.json'))).isFile()) throw new Error('The packaged full-stack starter is missing')\n}\n\nasync function assertEmptyDestination(destination: string) {\n try {\n const info = await stat(destination)\n if (!info.isDirectory() || (await readdir(destination)).length > 0)\n throw new Error(`Destination must be an empty directory: ${destination}`)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n}\n\nfunction packageName(name: string) {\n const normalized = name\n .toLowerCase()\n .replaceAll(/[^a-z\\d._-]/g, '-')\n .replaceAll(/^-+|-+$/g, '')\n return normalized || 'ras-stack-app'\n}\n"]}
@@ -1,16 +0,0 @@
1
- APP_URL=http://localhost:3100
2
- DATA_DIR=.data/example-full-stack
3
- TRUST_PROXY=false
4
- CENTRIFUGO_API_URL=http://127.0.0.1:8100/api
5
- CENTRIFUGO_API_KEY=replace-for-local-development
6
- CENTRIFUGO_PROXY_SECRET=replace-for-local-development
7
- BETTER_AUTH_SECRET=replace-with-at-least-32-random-characters
8
- SMTP_HOST=127.0.0.1
9
- SMTP_PORT=1025
10
- SMTP_SECURE=false
11
- EMAIL_FROM=ras-stack@example.test
12
- EMAIL_REQUIRE_VERIFICATION=true
13
- UPLOAD_MAX_BYTES=1000000
14
- UPLOAD_QUOTA_BYTES=5000000
15
- UPLOAD_GLOBAL_QUOTA_BYTES=100000000
16
- UPLOAD_GLOBAL_MAX_FILES=1024
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
- "singleQuote": true,
4
- "semi": false,
5
- "printWidth": 140,
6
- "ignorePatterns": ["src/routeTree.gen.ts", ".output/**", "node_modules/**"]
7
- }
@@ -1,37 +0,0 @@
1
- # syntax=docker/dockerfile:1
2
- ARG RUNTIME_BINARIES_IMAGE=ghcr.io/richardsolomou/ras-stack-runtime-binaries:runtime-v1.0.4@sha256:183261400ef822d3dfb7ae9391dc3836d343e01de41ab131df6a42751f87288e
3
- FROM node:24-alpine AS build
4
- WORKDIR /app
5
- RUN apk add --no-cache python3 make g++
6
- RUN corepack enable && corepack install --global pnpm@11.15.0
7
- COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
8
- COPY packages/ras-stack/package.json ./packages/ras-stack/package.json
9
- COPY packages/create-ras-app/package.json ./packages/create-ras-app/package.json
10
- COPY apps/web/package.json ./apps/web/package.json
11
- COPY examples/full-stack/package.json ./examples/full-stack/package.json
12
- RUN pnpm fetch --frozen-lockfile
13
- COPY packages/ras-stack ./packages/ras-stack
14
- COPY examples/full-stack ./examples/full-stack
15
- RUN pnpm install --offline --frozen-lockfile
16
- RUN pnpm --filter ras-stack build && pnpm --filter @ras-stack/example-full-stack build
17
-
18
- FROM ${RUNTIME_BINARIES_IMAGE} AS runtime-binaries
19
-
20
- FROM node:24-alpine
21
- WORKDIR /app
22
- RUN apk upgrade --no-cache \
23
- && apk add --no-cache tini \
24
- && rm -rf /usr/local/lib/node_modules/corepack /usr/local/lib/node_modules/npm /opt/yarn-v1.22.22 \
25
- && rm -f /usr/local/bin/corepack /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/yarn /usr/local/bin/yarnpkg \
26
- && mkdir -p /data \
27
- && chown -R node:node /app /data
28
- COPY --from=build --chown=node:node /app/examples/full-stack/.output ./.output
29
- COPY --from=runtime-binaries /usr/local/bin/centrifugo /usr/local/bin/centrifugo
30
- COPY --from=runtime-binaries /usr/local/bin/caddy /usr/local/bin/caddy
31
- COPY --chown=node:node examples/full-stack/centrifugo.json ./centrifugo.json
32
- COPY --chown=node:node packages/ras-stack/LICENSE ./
33
- ENV NODE_ENV=production DATA_DIR=/data TRUST_PROXY=false CENTRIFUGO_CONFIG=/app/centrifugo.json CENTRIFUGO_API_URL=http://127.0.0.1:8100/api CENTRIFUGO_HTTP_SERVER_PORT=8100
34
- EXPOSE 3100
35
- HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 CMD wget -q --spider http://127.0.0.1:3100/api/ready || exit 1
36
- USER node
37
- ENTRYPOINT ["/sbin/tini", "-g", "--", "node", ".output/server/containerRuntime.mjs"]
@@ -1,29 +0,0 @@
1
- # syntax=docker/dockerfile:1
2
- ARG RUNTIME_BINARIES_IMAGE=ghcr.io/richardsolomou/ras-stack-runtime-binaries:runtime-v1.0.4@sha256:183261400ef822d3dfb7ae9391dc3836d343e01de41ab131df6a42751f87288e
3
- FROM node:24-alpine AS build
4
- WORKDIR /app
5
- RUN apk add --no-cache python3 make g++
6
- RUN corepack enable && corepack install --global pnpm@11.15.0
7
- COPY . .
8
- RUN pnpm install --no-frozen-lockfile
9
- RUN pnpm build
10
-
11
- FROM ${RUNTIME_BINARIES_IMAGE} AS runtime-binaries
12
-
13
- FROM node:24-alpine
14
- WORKDIR /app
15
- RUN apk upgrade --no-cache \
16
- && apk add --no-cache tini \
17
- && rm -rf /usr/local/lib/node_modules/corepack /usr/local/lib/node_modules/npm /opt/yarn-v1.22.22 \
18
- && rm -f /usr/local/bin/corepack /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/yarn /usr/local/bin/yarnpkg \
19
- && mkdir -p /data \
20
- && chown -R node:node /app /data
21
- COPY --from=build --chown=node:node /app/.output ./.output
22
- COPY --from=runtime-binaries /usr/local/bin/centrifugo /usr/local/bin/centrifugo
23
- COPY --from=runtime-binaries /usr/local/bin/caddy /usr/local/bin/caddy
24
- COPY --chown=node:node centrifugo.json ./centrifugo.json
25
- ENV NODE_ENV=production DATA_DIR=/data TRUST_PROXY=false CENTRIFUGO_CONFIG=/app/centrifugo.json CENTRIFUGO_API_URL=http://127.0.0.1:8100/api CENTRIFUGO_HTTP_SERVER_PORT=8100
26
- EXPOSE 3100
27
- HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 CMD wget -q --spider http://127.0.0.1:3100/api/ready || exit 1
28
- USER node
29
- ENTRYPOINT ["/sbin/tini", "-g", "--", "node", ".output/server/containerRuntime.mjs"]
@@ -1,14 +0,0 @@
1
- {
2
- "client": {
3
- "proxy": {
4
- "connect": {
5
- "enabled": true,
6
- "endpoint": "http://127.0.0.1:3101/api/centrifugo/connect",
7
- "http_headers": ["Cookie", "Origin", "X-Forwarded-Host", "X-Forwarded-Proto"],
8
- "http": { "static_headers": { "X-Proxy-Secret": "${CENTRIFUGO_VAR_PROXY_SECRET}" } }
9
- }
10
- }
11
- },
12
- "channel": { "namespaces": [{ "name": "messages", "allow_subscribe_for_client": true }] },
13
- "health": { "enabled": true }
14
- }
@@ -1,8 +0,0 @@
1
- .data
2
- .git
3
- .output
4
- node_modules
5
- test-results
6
- .env
7
- .env.*
8
- !.env.example
@@ -1,114 +0,0 @@
1
- CREATE TABLE `user` (
2
- `id` text PRIMARY KEY NOT NULL,
3
- `name` text NOT NULL,
4
- `email` text NOT NULL,
5
- `email_verified` integer DEFAULT false NOT NULL,
6
- `image` text,
7
- `created_at` integer NOT NULL,
8
- `updated_at` integer NOT NULL
9
- );
10
- --> statement-breakpoint
11
- CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);
12
- --> statement-breakpoint
13
- CREATE TABLE `session` (
14
- `id` text PRIMARY KEY NOT NULL,
15
- `expires_at` integer NOT NULL,
16
- `token` text NOT NULL,
17
- `created_at` integer NOT NULL,
18
- `updated_at` integer NOT NULL,
19
- `ip_address` text,
20
- `user_agent` text,
21
- `user_id` text NOT NULL,
22
- FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
23
- );
24
- --> statement-breakpoint
25
- CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);
26
- --> statement-breakpoint
27
- CREATE INDEX `session_user_id_idx` ON `session` (`user_id`);
28
- --> statement-breakpoint
29
- CREATE TABLE `account` (
30
- `id` text PRIMARY KEY NOT NULL,
31
- `account_id` text NOT NULL,
32
- `issuer` text NOT NULL,
33
- `provider_id` text NOT NULL,
34
- `user_id` text NOT NULL,
35
- `access_token` text,
36
- `refresh_token` text,
37
- `id_token` text,
38
- `access_token_expires_at` integer,
39
- `refresh_token_expires_at` integer,
40
- `scope` text,
41
- `password` text,
42
- `created_at` integer NOT NULL,
43
- `updated_at` integer NOT NULL,
44
- FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
45
- );
46
- --> statement-breakpoint
47
- CREATE INDEX `account_user_id_idx` ON `account` (`user_id`);
48
- --> statement-breakpoint
49
- CREATE UNIQUE INDEX `account_issuer_account_id_unique` ON `account` (`issuer`,`account_id`);
50
- --> statement-breakpoint
51
- CREATE TABLE `verification` (
52
- `id` text PRIMARY KEY NOT NULL,
53
- `identifier` text NOT NULL,
54
- `value` text NOT NULL,
55
- `expires_at` integer NOT NULL,
56
- `created_at` integer NOT NULL,
57
- `updated_at` integer NOT NULL
58
- );
59
- --> statement-breakpoint
60
- CREATE INDEX `verification_identifier_idx` ON `verification` (`identifier`);
61
- --> statement-breakpoint
62
- CREATE TABLE `rate_limit` (
63
- `id` text PRIMARY KEY NOT NULL,
64
- `key` text NOT NULL,
65
- `count` integer NOT NULL,
66
- `last_request` integer NOT NULL
67
- );
68
- --> statement-breakpoint
69
- CREATE UNIQUE INDEX `rate_limit_key_unique` ON `rate_limit` (`key`);
70
- --> statement-breakpoint
71
- CREATE TABLE `app_rate_limit` (
72
- `key` text PRIMARY KEY NOT NULL,
73
- `count` integer NOT NULL,
74
- `reset_at` integer NOT NULL
75
- );
76
- --> statement-breakpoint
77
- CREATE TABLE `messages` (
78
- `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
79
- `author_id` text NOT NULL,
80
- `author` text NOT NULL,
81
- `body` text NOT NULL,
82
- `created_at` integer NOT NULL,
83
- FOREIGN KEY (`author_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
84
- );
85
- --> statement-breakpoint
86
- CREATE INDEX `messages_author_id_idx` ON `messages` (`author_id`);
87
- --> statement-breakpoint
88
- CREATE TABLE `uploads` (
89
- `id` text PRIMARY KEY NOT NULL,
90
- `owner_id` text NOT NULL,
91
- `filename` text NOT NULL,
92
- `media_type` text NOT NULL,
93
- `length` integer NOT NULL,
94
- `offset` integer DEFAULT 0 NOT NULL,
95
- `state` text DEFAULT 'active' NOT NULL,
96
- `created_at` integer NOT NULL,
97
- `updated_at` integer NOT NULL,
98
- FOREIGN KEY (`owner_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
99
- );
100
- --> statement-breakpoint
101
- CREATE INDEX `uploads_owner_id_state_idx` ON `uploads` (`owner_id`,`state`);
102
- --> statement-breakpoint
103
- CREATE TABLE `outbox` (
104
- `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
105
- `channel` text NOT NULL,
106
- `payload` text NOT NULL,
107
- `attempts` integer DEFAULT 0 NOT NULL,
108
- `available_at` integer NOT NULL,
109
- `created_at` integer NOT NULL,
110
- `failed_at` integer,
111
- `last_error` text
112
- );
113
- --> statement-breakpoint
114
- CREATE INDEX `outbox_available_at_idx` ON `outbox` (`available_at`,`id`);
@@ -1,13 +0,0 @@
1
- {
2
- "version": "7",
3
- "dialect": "sqlite",
4
- "entries": [
5
- {
6
- "idx": 0,
7
- "version": "6",
8
- "when": 1787072400000,
9
- "tag": "0000_production_reference",
10
- "breakpoints": true
11
- }
12
- ]
13
- }
@@ -1,54 +0,0 @@
1
- import { expect, test } from '@playwright/test'
2
-
3
- test('keeps authentication submission disabled before hydration', async ({ browser }) => {
4
- const context = await browser.newContext({ javaScriptEnabled: false })
5
- const page = await context.newPage()
6
- await page.goto('/')
7
-
8
- await expect(page.getByRole('button', { name: 'Create account', exact: true })).toBeDisabled()
9
- await context.close()
10
- })
11
-
12
- test('signs up, authorizes resources, persists an upload, and receives an outbox publication', async ({ browser, request }, testInfo) => {
13
- const run = Date.now()
14
- const firstMessage = `first message ${run}`
15
- const realtimeMessage = `realtime message ${run}`
16
- const origin = testInfo.project.use.baseURL!
17
- expect((await request.get('/api/live')).status()).toBe(200)
18
- expect((await request.get('/api/ready')).status()).toBe(200)
19
- expect((await request.get('/')).headers()['x-content-type-options']).toBe('nosniff')
20
- expect((await request.post('/api/uploads', { headers: { Origin: origin, 'Upload-Length': '1' } })).status()).toBe(401)
21
- expect((await request.head('/api/uploads/missing')).status()).toBe(401)
22
-
23
- const alice = await browser.newPage()
24
- await alice.goto('/')
25
- await signUp(alice, 'Alice', 'alice@example.test')
26
- await alice.getByLabel('Message').fill(firstMessage)
27
- await alice.getByRole('button', { name: 'Add message' }).click()
28
- await expect(alice.getByText(`Alice: ${firstMessage}`)).toBeVisible()
29
-
30
- await alice.getByLabel('Upload').setInputFiles({ name: 'proof.txt', mimeType: 'text/plain', buffer: Buffer.from('workspace proof') })
31
- const upload = alice.getByText(/Uploaded to .*\/api\/uploads\//)
32
- await expect(upload).toBeVisible()
33
- const uploadUrl = (await upload.textContent())!.replace('Uploaded to ', '')
34
-
35
- const bob = await browser.newPage()
36
- await bob.goto('/')
37
- await signUp(bob, 'Bob', 'bob@example.test')
38
- expect(await bob.evaluate((url) => fetch(url, { method: 'HEAD' }).then((response) => response.status), uploadUrl)).toBe(404)
39
- await expect(bob.getByText(`Alice: ${firstMessage}`)).toBeVisible()
40
- await bob.getByLabel('Message').fill(realtimeMessage)
41
- await bob.getByRole('button', { name: 'Add message' }).click()
42
- await expect(alice.getByText(`Bob: ${realtimeMessage}`)).toBeVisible()
43
- await alice.getByRole('button', { name: 'Sign out' }).click()
44
- await expect(alice.getByRole('heading', { name: 'Create account' })).toBeVisible()
45
- await alice.screenshot({ path: testInfo.outputPath('example.png'), fullPage: true })
46
- })
47
-
48
- async function signUp(page: import('@playwright/test').Page, name: string, email: string) {
49
- await page.getByLabel('Name').fill(name)
50
- await page.getByLabel('Email').fill(email)
51
- await page.getByLabel('Password').fill('correct horse battery staple')
52
- await page.getByRole('button', { name: 'Create account', exact: true }).click()
53
- await expect(page.getByRole('heading', { name: `Hello, ${name}` })).toBeVisible()
54
- }
@@ -1,9 +0,0 @@
1
- dist/
2
- node_modules/
3
- .data/
4
- .output/
5
- test-results/
6
- *.tgz
7
- .env
8
- .env.*
9
- !.env.example
@@ -1,4 +0,0 @@
1
- {
2
- "extends": ["./node_modules/ras-stack/config/oxlint/application.json", "./node_modules/ras-stack/config/oxlint/tanstack.json"],
3
- "ignorePatterns": ["src/routeTree.gen.ts", ".output/**", "node_modules/**"]
4
- }
@@ -1,60 +0,0 @@
1
- {
2
- "name": "@ras-stack/example-full-stack",
3
- "private": true,
4
- "type": "module",
5
- "scripts": {
6
- "build": "vite build && node ../../packages/ras-stack/dist/cli.js assets sync && esbuild scripts/containerRuntime.ts scripts/database.ts --bundle --platform=node --format=esm --target=node24 --external:better-sqlite3 --outdir=.output/server --out-extension:.js=.mjs --log-level=warning && node ../../packages/ras-stack/dist/cli.js assets check",
7
- "dev": "vite dev",
8
- "format": "oxfmt --write .",
9
- "format:check": "oxfmt --check .",
10
- "lint": "oxlint --config oxlint.json --type-aware --deny-warnings .",
11
- "realtime": "ras realtime --config centrifugo.json --name ras-stack-example-realtime --port 8100 --origin http://localhost:3100 --secret example-development-secret --connect-proxy-endpoint http://host.docker.internal:3100/api/centrifugo/connect",
12
- "e2e": "playwright test",
13
- "test": "vitest run --config vitest.config.ts",
14
- "typecheck": "tsc --noEmit",
15
- "db:backup": "node .output/server/database.mjs backup",
16
- "db:restore": "node .output/server/database.mjs restore",
17
- "check": "pnpm format:check && pnpm lint && pnpm build && pnpm typecheck && pnpm test"
18
- },
19
- "dependencies": {
20
- "@better-auth/core": "1.7.1",
21
- "@better-auth/drizzle-adapter": "1.7.1",
22
- "@opentelemetry/api-logs": "^0.221.0",
23
- "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
24
- "@opentelemetry/resources": "^2.10.0",
25
- "@opentelemetry/sdk-logs": "^0.221.0",
26
- "@posthog/react": "^1.10.6",
27
- "@tanstack/react-query": "^5.101.4",
28
- "@tanstack/react-router": "^1.170.18",
29
- "@tanstack/react-router-ssr-query": "^1.167.1",
30
- "@tanstack/react-start": "^1.168.32",
31
- "better-auth": "1.7.1",
32
- "better-sqlite3": "12.11.1",
33
- "centrifuge": "5.7.1",
34
- "drizzle-orm": "0.45.2",
35
- "nodemailer": "^9.0.3",
36
- "posthog-js": "^1.430.3",
37
- "posthog-node": "^5.52.1",
38
- "ras-stack": "workspace:*",
39
- "react": "19.2.8",
40
- "react-dom": "19.2.8",
41
- "tus-js-client": "^4.3.1",
42
- "zod": "4.4.3"
43
- },
44
- "devDependencies": {
45
- "@playwright/test": "1.62.1",
46
- "@types/better-sqlite3": "9.6.0",
47
- "@types/node": "^26.2.0",
48
- "@types/react": "19.2.18",
49
- "@types/react-dom": "19.2.3",
50
- "@vitejs/plugin-react": "^6.0.5",
51
- "esbuild": "0.28.2",
52
- "nitro": "3.0.260610-beta",
53
- "oxfmt": "^0.63.0",
54
- "oxlint": "^1.74.0",
55
- "oxlint-tsgolint": "^7.0.2001",
56
- "typescript": "^7.0.2",
57
- "vite": "8.2.1",
58
- "vitest": "^4.1.10"
59
- }
60
- }
@@ -1,7 +0,0 @@
1
- import { defineConfig, devices } from '@playwright/test'
2
-
3
- export default defineConfig({
4
- testDir: './e2e',
5
- timeout: 30_000,
6
- use: { ...devices['Desktop Chrome'], baseURL: process.env.EXAMPLE_BASE_URL ?? 'http://127.0.0.1:3110', trace: 'retain-on-failure' },
7
- })
@@ -1,13 +0,0 @@
1
- packages:
2
- - .
3
- allowBuilds:
4
- better-sqlite3: true
5
- core-js: false
6
- esbuild: true
7
- protobufjs: true
8
- minimumReleaseAge: 10080
9
- minimumReleaseAgeExclude:
10
- - create-ras-app
11
- - ras-stack
12
- - better-auth
13
- - '@better-auth/*'
@@ -1,4 +0,0 @@
1
- {
2
- "outputDirectory": ".output/server",
3
- "assets": [{ "source": "drizzle", "destination": "drizzle" }]
4
- }
@@ -1,29 +0,0 @@
1
- import { runRealtimeStack } from 'ras-stack/runtime'
2
-
3
- const required = (name: string) => {
4
- const value = process.env[name]?.trim()
5
- if (!value) throw new Error(`${name} is required`)
6
- return value
7
- }
8
-
9
- const apiKey = required('CENTRIFUGO_API_KEY')
10
- const proxySecret = required('CENTRIFUGO_PROXY_SECRET')
11
- const realtime: NodeJS.ProcessEnv = { ...process.env, CENTRIFUGO_VAR_PROXY_SECRET: proxySecret }
12
- delete realtime.CENTRIFUGO_API_KEY
13
- delete realtime.CENTRIFUGO_PROXY_SECRET
14
- delete realtime.CENTRIFUGO_API_URL
15
- delete realtime.CENTRIFUGO_CONFIG
16
-
17
- process.exitCode = await runRealtimeStack({
18
- app: { command: process.execPath, args: ['.output/server/index.mjs'], env: { ...process.env, PORT: '3101' } },
19
- centrifugo: {
20
- configPath: process.env.CENTRIFUGO_CONFIG ?? '/app/centrifugo.json',
21
- env: realtime,
22
- environment: { apiKey, allowedOrigins: process.env.APP_URL ?? 'http://localhost:3100' },
23
- },
24
- caddy: {
25
- configPath: '/tmp/ras-stack-example-Caddyfile',
26
- env: process.env,
27
- proxy: { publicPort: 3100, appPort: 3101, realtimePort: 8100 },
28
- },
29
- })