create-react-native-airborne 0.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.
- package/README.md +24 -0
- package/package.json +21 -0
- package/src/index.mjs +103 -0
- package/template/.agents/skills/convex-best-practices/SKILL.md +333 -0
- package/template/.agents/skills/convex-file-storage/SKILL.md +466 -0
- package/template/.agents/skills/convex-security-audit/SKILL.md +538 -0
- package/template/.agents/skills/convex-security-check/SKILL.md +377 -0
- package/template/.github/workflows/ci.yml +130 -0
- package/template/.prettierignore +8 -0
- package/template/.prettierrc.json +6 -0
- package/template/AGENTS.md +156 -0
- package/template/Justfile +48 -0
- package/template/README.md +94 -0
- package/template/client/.env.example +3 -0
- package/template/client/.vscode/extensions.json +1 -0
- package/template/client/.vscode/settings.json +7 -0
- package/template/client/README.md +33 -0
- package/template/client/app/(app)/_layout.tsx +34 -0
- package/template/client/app/(app)/index.tsx +66 -0
- package/template/client/app/(app)/push.tsx +75 -0
- package/template/client/app/(app)/settings.tsx +36 -0
- package/template/client/app/(auth)/_layout.tsx +22 -0
- package/template/client/app/(auth)/sign-in.tsx +358 -0
- package/template/client/app/(auth)/sign-up.tsx +237 -0
- package/template/client/app/_layout.tsx +30 -0
- package/template/client/app/index.tsx +127 -0
- package/template/client/app.config.ts +30 -0
- package/template/client/assets/images/android-icon-background.png +0 -0
- package/template/client/assets/images/android-icon-foreground.png +0 -0
- package/template/client/assets/images/android-icon-monochrome.png +0 -0
- package/template/client/assets/images/favicon.png +0 -0
- package/template/client/assets/images/icon.png +0 -0
- package/template/client/assets/images/partial-react-logo.png +0 -0
- package/template/client/assets/images/react-logo.png +0 -0
- package/template/client/assets/images/react-logo@2x.png +0 -0
- package/template/client/assets/images/react-logo@3x.png +0 -0
- package/template/client/assets/images/splash-icon.png +0 -0
- package/template/client/eslint.config.js +10 -0
- package/template/client/global.css +2 -0
- package/template/client/metro.config.js +9 -0
- package/template/client/package.json +51 -0
- package/template/client/src/components/auth-shell.tsx +63 -0
- package/template/client/src/components/form-input.tsx +62 -0
- package/template/client/src/components/primary-button.tsx +37 -0
- package/template/client/src/components/screen.tsx +17 -0
- package/template/client/src/components/sign-out-button.tsx +32 -0
- package/template/client/src/hooks/use-theme-sync.ts +11 -0
- package/template/client/src/lib/convex.ts +6 -0
- package/template/client/src/lib/env-schema.ts +13 -0
- package/template/client/src/lib/env.test.ts +24 -0
- package/template/client/src/lib/env.ts +19 -0
- package/template/client/src/lib/notifications.ts +47 -0
- package/template/client/src/store/preferences-store.ts +42 -0
- package/template/client/src/types/theme.ts +1 -0
- package/template/client/tsconfig.json +18 -0
- package/template/client/uniwind-types.d.ts +10 -0
- package/template/client/vitest.config.ts +7 -0
- package/template/package.json +22 -0
- package/template/server/.env.example +8 -0
- package/template/server/README.md +31 -0
- package/template/server/convex/_generated/api.d.ts +55 -0
- package/template/server/convex/_generated/api.js +23 -0
- package/template/server/convex/_generated/dataModel.d.ts +60 -0
- package/template/server/convex/_generated/server.d.ts +143 -0
- package/template/server/convex/_generated/server.js +93 -0
- package/template/server/convex/auth.config.ts +11 -0
- package/template/server/convex/env.ts +18 -0
- package/template/server/convex/lib.ts +12 -0
- package/template/server/convex/push.ts +148 -0
- package/template/server/convex/schema.ts +22 -0
- package/template/server/convex/users.ts +54 -0
- package/template/server/convex.json +3 -0
- package/template/server/eslint.config.js +51 -0
- package/template/server/package.json +29 -0
- package/template/server/tests/convex.test.ts +52 -0
- package/template/server/tests/import-meta.d.ts +3 -0
- package/template/server/tsconfig.json +15 -0
- package/template/server/vitest.config.ts +13 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query, type MutationCtx, type QueryCtx } from "./_generated/server";
|
|
3
|
+
import { requireUserIdentity } from "./lib";
|
|
4
|
+
|
|
5
|
+
async function getUserByClerkId(ctx: MutationCtx | QueryCtx, clerkUserId: string) {
|
|
6
|
+
return ctx.db
|
|
7
|
+
.query("users")
|
|
8
|
+
.withIndex("by_clerk_user_id", (q) => q.eq("clerkUserId", clerkUserId))
|
|
9
|
+
.unique();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const bootstrap = mutation({
|
|
13
|
+
args: {
|
|
14
|
+
email: v.optional(v.string()),
|
|
15
|
+
name: v.optional(v.string()),
|
|
16
|
+
imageUrl: v.optional(v.string()),
|
|
17
|
+
},
|
|
18
|
+
handler: async (ctx, args) => {
|
|
19
|
+
const identity = await requireUserIdentity(ctx);
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
|
|
22
|
+
const existing = await getUserByClerkId(ctx, identity.subject);
|
|
23
|
+
|
|
24
|
+
if (existing) {
|
|
25
|
+
await ctx.db.patch("users", existing._id, {
|
|
26
|
+
email: args.email ?? existing.email,
|
|
27
|
+
name: args.name ?? existing.name,
|
|
28
|
+
imageUrl: args.imageUrl ?? existing.imageUrl,
|
|
29
|
+
lastSeenAt: now,
|
|
30
|
+
});
|
|
31
|
+
return existing._id;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return ctx.db.insert("users", {
|
|
35
|
+
clerkUserId: identity.subject,
|
|
36
|
+
email: args.email,
|
|
37
|
+
name: args.name,
|
|
38
|
+
imageUrl: args.imageUrl,
|
|
39
|
+
lastSeenAt: now,
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const current = query({
|
|
45
|
+
args: {},
|
|
46
|
+
handler: async (ctx) => {
|
|
47
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
48
|
+
if (!identity) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return getUserByClerkId(ctx, identity.subject);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { defineConfig } from "eslint/config";
|
|
2
|
+
import js from "@eslint/js";
|
|
3
|
+
import convexPlugin from "@convex-dev/eslint-plugin";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
|
|
6
|
+
export default defineConfig(
|
|
7
|
+
{
|
|
8
|
+
ignores: [
|
|
9
|
+
"node_modules/**",
|
|
10
|
+
"dist/**",
|
|
11
|
+
"coverage/**",
|
|
12
|
+
"convex/_generated/**",
|
|
13
|
+
"eslint.config.js",
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
js.configs.recommended,
|
|
17
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
18
|
+
...tseslint.configs.strictTypeChecked,
|
|
19
|
+
{
|
|
20
|
+
languageOptions: {
|
|
21
|
+
parserOptions: {
|
|
22
|
+
projectService: true,
|
|
23
|
+
tsconfigRootDir: import.meta.dirname,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
rules: {
|
|
27
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
28
|
+
"error",
|
|
29
|
+
{ prefer: "type-imports", fixStyle: "inline-type-imports" },
|
|
30
|
+
],
|
|
31
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
32
|
+
"@typescript-eslint/require-await": "off",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
...convexPlugin.configs.recommended,
|
|
36
|
+
{
|
|
37
|
+
files: ["convex/**/*.ts"],
|
|
38
|
+
rules: {
|
|
39
|
+
"@convex-dev/import-wrong-runtime": "warn",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
files: ["tests/**/*.ts"],
|
|
44
|
+
rules: {
|
|
45
|
+
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
46
|
+
"@typescript-eslint/no-unsafe-call": "off",
|
|
47
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
48
|
+
"@typescript-eslint/no-unsafe-return": "off",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "convex dev",
|
|
8
|
+
"codegen": "convex codegen",
|
|
9
|
+
"lint": "eslint . --max-warnings 0",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"convex": "^1.28.2",
|
|
16
|
+
"zod": "^3.25.76"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@convex-dev/eslint-plugin": "^1.1.1",
|
|
20
|
+
"@eslint/js": "^9.38.0",
|
|
21
|
+
"@edge-runtime/vm": "^5.0.0",
|
|
22
|
+
"@types/node": "^24.3.0",
|
|
23
|
+
"convex-test": "^0.0.38",
|
|
24
|
+
"eslint": "^9.38.0",
|
|
25
|
+
"typescript": "^5.9.2",
|
|
26
|
+
"typescript-eslint": "^8.46.2",
|
|
27
|
+
"vitest": "^3.2.4"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { convexTest } from "convex-test";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import schema from "../convex/schema";
|
|
4
|
+
import { api } from "../convex/_generated/api";
|
|
5
|
+
|
|
6
|
+
const modules = import.meta.glob("../convex/**/*.ts");
|
|
7
|
+
|
|
8
|
+
describe("convex functions", () => {
|
|
9
|
+
test("users.bootstrap creates a user and can be queried", async () => {
|
|
10
|
+
const t = convexTest(schema, modules);
|
|
11
|
+
|
|
12
|
+
const authed = t.withIdentity({
|
|
13
|
+
subject: "user_123",
|
|
14
|
+
issuer: "https://example.clerk.accounts.dev",
|
|
15
|
+
tokenIdentifier: "test|user_123",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
await authed.mutation(api.users.bootstrap, {
|
|
19
|
+
email: "test@example.com",
|
|
20
|
+
name: "Airborne User",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const current = await authed.query(api.users.current, {});
|
|
24
|
+
expect(current?.clerkUserId).toBe("user_123");
|
|
25
|
+
expect(current?.email).toBe("test@example.com");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("push token register and unregister", async () => {
|
|
29
|
+
const t = convexTest(schema, modules);
|
|
30
|
+
const authed = t.withIdentity({
|
|
31
|
+
subject: "user_456",
|
|
32
|
+
issuer: "https://example.clerk.accounts.dev",
|
|
33
|
+
tokenIdentifier: "test|user_456",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await authed.mutation(api.users.bootstrap, {});
|
|
37
|
+
await authed.mutation(api.push.registerToken, {
|
|
38
|
+
token: "ExponentPushToken[example]",
|
|
39
|
+
platform: "ios",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const tokens = await authed.query(api.push.listMyTokens, {});
|
|
43
|
+
expect(tokens).toHaveLength(1);
|
|
44
|
+
|
|
45
|
+
await authed.mutation(api.push.unregisterToken, {
|
|
46
|
+
token: "ExponentPushToken[example]",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const after = await authed.query(api.push.listMyTokens, {});
|
|
50
|
+
expect(after).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"types": ["node", "vitest/globals"],
|
|
10
|
+
"allowSyntheticDefaultImports": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["convex/**/*.ts", "tests/**/*.ts", "tests/**/*.d.ts", "vitest.config.ts"]
|
|
15
|
+
}
|