lazyconvex 0.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 (70) hide show
  1. package/README.md +926 -0
  2. package/dist/components/index.mjs +937 -0
  3. package/dist/error-D4GuI0ot.mjs +71 -0
  4. package/dist/file-field-BqVgy8xY.mjs +205 -0
  5. package/dist/form-BXJK_j10.d.mts +99 -0
  6. package/dist/index.d.mts +433 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/index2.d.mts +5 -0
  9. package/dist/index3.d.mts +35 -0
  10. package/dist/index4.d.mts +101 -0
  11. package/dist/index5.d.mts +842 -0
  12. package/dist/next/index.mjs +151 -0
  13. package/dist/org-CmJBb8z-.d.mts +56 -0
  14. package/dist/react/index.mjs +158 -0
  15. package/dist/retry.d.mts +12 -0
  16. package/dist/retry.mjs +35 -0
  17. package/dist/schema.d.mts +23 -0
  18. package/dist/schema.mjs +15 -0
  19. package/dist/server/index.mjs +2572 -0
  20. package/dist/types-DWBVRtit.d.mts +322 -0
  21. package/dist/use-online-status-CMr73Jlk.mjs +155 -0
  22. package/dist/use-upload-DtELytQi.mjs +95 -0
  23. package/dist/zod.d.mts +18 -0
  24. package/dist/zod.mjs +87 -0
  25. package/package.json +40 -0
  26. package/src/components/editors-section.tsx +86 -0
  27. package/src/components/fields.tsx +884 -0
  28. package/src/components/file-field.tsx +234 -0
  29. package/src/components/form.tsx +191 -0
  30. package/src/components/index.ts +11 -0
  31. package/src/components/offline-indicator.tsx +15 -0
  32. package/src/components/org-avatar.tsx +13 -0
  33. package/src/components/permission-guard.tsx +36 -0
  34. package/src/components/role-badge.tsx +14 -0
  35. package/src/components/suspense-wrap.tsx +8 -0
  36. package/src/index.ts +40 -0
  37. package/src/next/active-org.ts +33 -0
  38. package/src/next/auth.ts +9 -0
  39. package/src/next/image.ts +134 -0
  40. package/src/next/index.ts +3 -0
  41. package/src/react/form-meta.ts +53 -0
  42. package/src/react/form.ts +201 -0
  43. package/src/react/index.ts +8 -0
  44. package/src/react/org.tsx +96 -0
  45. package/src/react/use-active-org.ts +48 -0
  46. package/src/react/use-bulk-selection.ts +47 -0
  47. package/src/react/use-online-status.ts +21 -0
  48. package/src/react/use-optimistic.ts +54 -0
  49. package/src/react/use-upload.ts +101 -0
  50. package/src/retry.ts +47 -0
  51. package/src/schema.ts +30 -0
  52. package/src/server/cache-crud.ts +175 -0
  53. package/src/server/check-schema.ts +29 -0
  54. package/src/server/child.ts +98 -0
  55. package/src/server/crud.ts +384 -0
  56. package/src/server/db.ts +7 -0
  57. package/src/server/error.ts +39 -0
  58. package/src/server/file.ts +372 -0
  59. package/src/server/helpers.ts +214 -0
  60. package/src/server/index.ts +12 -0
  61. package/src/server/org-crud.ts +307 -0
  62. package/src/server/org-helpers.ts +54 -0
  63. package/src/server/org.ts +572 -0
  64. package/src/server/schema-helpers.ts +107 -0
  65. package/src/server/setup.ts +138 -0
  66. package/src/server/test-crud.ts +211 -0
  67. package/src/server/test.ts +554 -0
  68. package/src/server/types.ts +392 -0
  69. package/src/server/unique.ts +28 -0
  70. package/src/zod.ts +141 -0
@@ -0,0 +1,71 @@
1
+ import { ConvexError } from "convex/values";
2
+
3
+ //#region src/server/types.ts
4
+ const ERROR_MESSAGES = {
5
+ ALREADY_ORG_MEMBER: "Already a member of this organization",
6
+ CANNOT_MODIFY_ADMIN: "Admins cannot modify other admins",
7
+ CANNOT_MODIFY_OWNER: "Cannot modify the owner",
8
+ CHUNK_ALREADY_UPLOADED: "Chunk already uploaded",
9
+ CHUNK_NOT_FOUND: "Chunk not found",
10
+ CONFLICT: "Conflict detected",
11
+ EDITOR_REQUIRED: "Editor permission required",
12
+ FILE_NOT_FOUND: "File not found",
13
+ FILE_TOO_LARGE: "File too large",
14
+ FORBIDDEN: "Forbidden",
15
+ INCOMPLETE_UPLOAD: "Incomplete upload",
16
+ INSUFFICIENT_ORG_ROLE: "Insufficient permissions",
17
+ INVALID_FILE_TYPE: "Invalid file type",
18
+ INVALID_INVITE: "Invalid invite",
19
+ INVALID_MESSAGE: "Invalid message",
20
+ INVALID_SESSION_STATE: "Invalid session state",
21
+ INVALID_TOOL_ARGS: "Invalid tool arguments",
22
+ INVALID_WHERE: "Invalid filters",
23
+ INVITE_EXPIRED: "Invite has expired",
24
+ JOIN_REQUEST_EXISTS: "Join request already exists",
25
+ LIMIT_EXCEEDED: "Limit exceeded",
26
+ MESSAGE_NOT_SAVED: "Message not saved",
27
+ MUST_TRANSFER_OWNERSHIP: "Must transfer ownership before leaving",
28
+ NO_FETCHER: "No fetcher configured",
29
+ NO_PRECEDING_USER_MESSAGE: "No preceding user message",
30
+ NOT_AUTHENTICATED: "Please log in",
31
+ NOT_AUTHORIZED: "Not authorized",
32
+ NOT_FOUND: "Not found",
33
+ NOT_ORG_MEMBER: "Not a member of this organization",
34
+ ORG_SLUG_TAKEN: "Organization slug already taken",
35
+ RATE_LIMITED: "Too many requests",
36
+ SESSION_NOT_FOUND: "Session not found",
37
+ TARGET_MUST_BE_ADMIN: "Can only transfer ownership to an admin",
38
+ UNAUTHORIZED: "Unauthorized",
39
+ USER_NOT_FOUND: "User not found"
40
+ };
41
+
42
+ //#endregion
43
+ //#region src/server/error.ts
44
+ const isRecord = (v) => Boolean(v) && typeof v === "object", getErrorCode = (e) => {
45
+ if (!(e instanceof ConvexError)) return;
46
+ const { data } = e;
47
+ if (!isRecord(data)) return;
48
+ const { code } = data;
49
+ return typeof code === "string" && code in ERROR_MESSAGES ? code : void 0;
50
+ }, getErrorMessage = (e) => {
51
+ if (e instanceof ConvexError) {
52
+ const { data } = e;
53
+ if (isRecord(data)) {
54
+ if (typeof data.message === "string") return data.message;
55
+ const { code } = data;
56
+ if (typeof code === "string" && code in ERROR_MESSAGES) return ERROR_MESSAGES[code];
57
+ }
58
+ }
59
+ if (e instanceof Error) return e.message;
60
+ return "Unknown error";
61
+ }, handleConvexError = (e, handlers) => {
62
+ const code = getErrorCode(e), handler = code ? handlers[code] : void 0;
63
+ if (handler) {
64
+ handler();
65
+ return;
66
+ }
67
+ handlers.default?.();
68
+ };
69
+
70
+ //#endregion
71
+ export { getErrorMessage as n, isRecord as r, getErrorCode as t };
@@ -0,0 +1,205 @@
1
+ import { t as useUpload } from "./use-upload-DtELytQi.mjs";
2
+ import "node:module";
3
+ import { useQuery } from "convex/react";
4
+ import { createContext, use, useCallback } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { FileIcon, ImageIcon, Upload, X } from "lucide-react";
7
+ import { cn } from "@a/ui";
8
+ import { Field, FieldError, FieldLabel } from "@a/ui/field";
9
+ import { toast } from "sonner";
10
+ import imageCompression from "browser-image-compression";
11
+ import { useDropzone } from "react-dropzone";
12
+
13
+ //#region \0rolldown/runtime.js
14
+ var __defProp = Object.defineProperty;
15
+ var __exportAll = (all, no_symbols) => {
16
+ let target = {};
17
+ for (var name in all) {
18
+ __defProp(target, name, {
19
+ get: all[name],
20
+ enumerable: true
21
+ });
22
+ }
23
+ if (!no_symbols) {
24
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
25
+ }
26
+ return target;
27
+ };
28
+
29
+ //#endregion
30
+ //#region src/components/file-field.tsx
31
+ var file_field_exports = /* @__PURE__ */ __exportAll({
32
+ FileApiContext: () => FileApiContext,
33
+ FileApiProvider: () => FileApiProvider,
34
+ default: () => FileFieldImpl
35
+ });
36
+ const FileApiContext = createContext(null), FileApiProvider = ({ children, value }) => /* @__PURE__ */ jsx(FileApiContext, {
37
+ value,
38
+ children
39
+ }), useFileApi = () => {
40
+ const ctx = use(FileApiContext);
41
+ if (!ctx) throw new Error("FileApiProvider is required");
42
+ return ctx;
43
+ }, fmt = (n) => n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(1)} KB` : `${(n / 1048576).toFixed(1)} MB`, isImg = (t) => t.startsWith("image/"), parseAccept = (a) => a ? Object.fromEntries(a.split(",").map((t) => [t.trim(), []])) : void 0, compress = async (f, on) => on && f.type.startsWith("image/") ? imageCompression(f, {
44
+ maxSizeMB: 1,
45
+ maxWidthOrHeight: 1920,
46
+ useWebWorker: true
47
+ }).catch(() => f) : f, Preview = ({ id, onRemove }) => {
48
+ const { info } = useFileApi(), d = useQuery(info, { id });
49
+ if (!d) return /* @__PURE__ */ jsx("p", { className: "size-16 animate-pulse rounded-lg bg-muted" });
50
+ return /* @__PURE__ */ jsxs("div", {
51
+ className: "relative",
52
+ children: [d.contentType && isImg(d.contentType) && d.url ? /* @__PURE__ */ jsx("img", {
53
+ alt: "",
54
+ className: "size-16 rounded-lg object-cover",
55
+ height: 64,
56
+ src: d.url,
57
+ width: 64
58
+ }) : /* @__PURE__ */ jsxs("div", {
59
+ className: "flex size-16 flex-col items-center justify-center rounded-lg bg-muted text-xs",
60
+ children: [/* @__PURE__ */ jsx(FileIcon, { className: "size-6 text-muted-foreground" }), /* @__PURE__ */ jsx("span", {
61
+ className: "mt-1",
62
+ children: fmt(d.size)
63
+ })]
64
+ }), onRemove ? /* @__PURE__ */ jsx("button", {
65
+ className: "absolute -top-2 -right-2 rounded-full bg-destructive p-1 text-white transition-transform hover:scale-110",
66
+ onClick: onRemove,
67
+ type: "button",
68
+ children: /* @__PURE__ */ jsx(X, { className: "size-3" })
69
+ }) : null]
70
+ });
71
+ }, Progress = ({ v }) => /* @__PURE__ */ jsxs("div", {
72
+ className: "flex flex-col items-center",
73
+ children: [/* @__PURE__ */ jsx("div", {
74
+ className: "mb-2 h-2 w-32 overflow-hidden rounded-full bg-muted",
75
+ children: /* @__PURE__ */ jsx("div", {
76
+ className: "h-full bg-primary transition-all",
77
+ style: { width: `${v}%` }
78
+ })
79
+ }), /* @__PURE__ */ jsxs("span", {
80
+ className: "text-sm text-muted-foreground",
81
+ children: [v, "%"]
82
+ })]
83
+ }), FileFieldImpl = ({ accept, className, compressImg = true, "data-testid": testId, disabled, field: f, label, max, maxSize, multiple }) => {
84
+ const { upload: uploadRef } = useFileApi(), raw = f.state.value, vals = multiple ? raw ?? [] : raw ? [raw] : [], inv = f.state.meta.isTouched && !f.state.meta.isValid, canAdd = multiple ? !max || vals.length < max : !vals.length, { isUploading, progress, reset, upload } = useUpload(uploadRef), errorId = `${f.name}-error`, onDrop = useCallback(async (accepted) => {
85
+ if (multiple && max && vals.length + accepted.length > max) return toast.error(`Max ${max}`);
86
+ const ids = [];
87
+ for (const file of accepted) {
88
+ const res = await upload(await compress(file, compressImg));
89
+ if (res.ok) ids.push(res.storageId);
90
+ else if (res.code === "HTTP") toast.error(`${file.name}: Upload failed (${res.status})`);
91
+ else if (res.code === "ABORTED") toast.error(`${file.name}: Upload canceled`);
92
+ else if (res.code === "NETWORK") toast.error(`${file.name}: Network error`);
93
+ else if (res.code === "INVALID_RESPONSE") toast.error(`${file.name}: Invalid response`);
94
+ else if (res.code === "URL") toast.error(`${file.name}: Failed to start upload`);
95
+ }
96
+ if (multiple) f.handleChange([...vals, ...ids]);
97
+ else if (ids[0]) f.handleChange(ids[0]);
98
+ }, [
99
+ compressImg,
100
+ f,
101
+ max,
102
+ multiple,
103
+ upload,
104
+ vals
105
+ ]), { getInputProps, getRootProps, inputRef, isDragActive } = useDropzone({
106
+ accept: parseAccept(accept),
107
+ disabled: disabled ?? (isUploading || !canAdd),
108
+ maxSize,
109
+ multiple: Boolean(multiple),
110
+ onDrop,
111
+ onDropRejected: (r) => {
112
+ const code = r[0]?.errors[0]?.code;
113
+ if (code === "file-too-large" && maxSize) toast.error(`Max ${fmt(maxSize)}`);
114
+ else if (code === "file-invalid-type") toast.error("Invalid type");
115
+ else if (code === "too-many-files" && max) toast.error(`Max ${max}`);
116
+ }
117
+ }), dropCls = cn("flex cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none", multiple ? "size-16" : "p-6", isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25 hover:border-primary/50", (disabled ?? isUploading) && "cursor-not-allowed opacity-50", className), tid = testId ?? f.name;
118
+ return /* @__PURE__ */ jsxs(Field, {
119
+ "data-invalid": inv,
120
+ "data-testid": tid,
121
+ children: [
122
+ label ? /* @__PURE__ */ jsxs(FieldLabel, {
123
+ htmlFor: f.name,
124
+ children: [label, multiple && max ? /* @__PURE__ */ jsxs("span", {
125
+ className: "text-muted-foreground",
126
+ children: [
127
+ " ",
128
+ "(",
129
+ vals.length,
130
+ "/",
131
+ max,
132
+ ")"
133
+ ]
134
+ }) : null]
135
+ }) : null,
136
+ multiple ? /* @__PURE__ */ jsxs("div", {
137
+ className: "flex flex-wrap gap-2",
138
+ children: [vals.map((id, i) => /* @__PURE__ */ jsx(Preview, {
139
+ id,
140
+ onRemove: () => f.handleChange(vals.filter((_, j) => j !== i))
141
+ }, id)), canAdd ? /* @__PURE__ */ jsxs("div", {
142
+ ...getRootProps(),
143
+ "aria-label": "Upload file",
144
+ className: dropCls,
145
+ onKeyDown: (e) => {
146
+ if (e.key === "Enter" || e.key === " ") {
147
+ e.preventDefault();
148
+ inputRef.current?.click();
149
+ }
150
+ },
151
+ role: "button",
152
+ tabIndex: 0,
153
+ children: [/* @__PURE__ */ jsx("input", {
154
+ ...getInputProps(),
155
+ "aria-describedby": inv ? errorId : void 0,
156
+ "aria-invalid": inv
157
+ }), isUploading ? /* @__PURE__ */ jsxs("span", {
158
+ className: "text-xs",
159
+ children: [progress, "%"]
160
+ }) : /* @__PURE__ */ jsx(Upload, { className: "size-5 text-muted-foreground" })]
161
+ }) : null]
162
+ }) : vals[0] ? /* @__PURE__ */ jsx(Preview, {
163
+ id: vals[0],
164
+ onRemove: () => {
165
+ f.handleChange(null);
166
+ reset();
167
+ }
168
+ }) : /* @__PURE__ */ jsxs("div", {
169
+ ...getRootProps(),
170
+ "aria-label": "Upload file",
171
+ className: dropCls,
172
+ onKeyDown: (e) => {
173
+ if (e.key === "Enter" || e.key === " ") {
174
+ e.preventDefault();
175
+ inputRef.current?.click();
176
+ }
177
+ },
178
+ role: "button",
179
+ tabIndex: 0,
180
+ children: [/* @__PURE__ */ jsx("input", {
181
+ ...getInputProps(),
182
+ "aria-describedby": inv ? errorId : void 0,
183
+ "aria-invalid": inv
184
+ }), isUploading ? /* @__PURE__ */ jsx(Progress, { v: progress }) : /* @__PURE__ */ jsxs(Fragment, { children: [
185
+ accept?.includes("image") ? /* @__PURE__ */ jsx(ImageIcon, { className: "mb-2 size-8 text-muted-foreground" }) : /* @__PURE__ */ jsx(Upload, { className: "mb-2 size-8 text-muted-foreground" }),
186
+ /* @__PURE__ */ jsx("span", {
187
+ className: "text-sm text-muted-foreground",
188
+ children: "Click or drag"
189
+ }),
190
+ maxSize ? /* @__PURE__ */ jsxs("span", {
191
+ className: "mt-1 text-xs text-muted-foreground",
192
+ children: ["Max ", fmt(maxSize)]
193
+ }) : null
194
+ ] })]
195
+ }),
196
+ inv ? /* @__PURE__ */ jsx(FieldError, {
197
+ errors: f.state.meta.errors,
198
+ id: errorId
199
+ }) : null
200
+ ]
201
+ });
202
+ };
203
+
204
+ //#endregion
205
+ export { file_field_exports as i, FileApiProvider as n, FileFieldImpl as r, FileApiContext as t };
@@ -0,0 +1,99 @@
1
+ import { FunctionReference } from "convex/server";
2
+ import { ZodObject, ZodRawShape, output } from "zod/v4";
3
+ import { FormValidateOrFn, ReactFormExtendedApi } from "@tanstack/react-form";
4
+
5
+ //#region src/react/form-meta.d.ts
6
+ type FieldKind = 'boolean' | 'date' | 'file' | 'files' | 'number' | 'string' | 'stringArray' | 'unknown';
7
+ interface FieldMeta {
8
+ kind: FieldKind;
9
+ max?: number;
10
+ }
11
+ type FieldMetaMap = Record<string, FieldMeta>;
12
+ declare const getMeta: (s: unknown) => FieldMeta, buildMeta: (s: ZodObject<ZodRawShape>) => FieldMetaMap;
13
+ //#endregion
14
+ //#region src/react/form.d.ts
15
+ type Api<T extends Record<string, unknown>> = ReactFormExtendedApi<T, undefined, undefined, undefined, undefined, undefined, FormValidateOrFn<T>, undefined, undefined, undefined, undefined, unknown>;
16
+ interface ConflictData {
17
+ code: string;
18
+ current?: unknown;
19
+ incoming?: unknown;
20
+ }
21
+ interface FormReturn<T extends Record<string, unknown>, S extends ZodObject<ZodRawShape>> {
22
+ conflict: ConflictData | null;
23
+ error: Error | null;
24
+ instance: Api<T>;
25
+ isDirty: boolean;
26
+ isPending: boolean;
27
+ lastSaved: null | number;
28
+ meta: FieldMetaMap;
29
+ reset: (values?: T) => void;
30
+ resolveConflict: (action: 'cancel' | 'overwrite' | 'reload') => void;
31
+ schema: S;
32
+ watch: <K extends keyof T>(name: K) => T[K];
33
+ }
34
+ declare const useForm$1: <S extends ZodObject<ZodRawShape>>({
35
+ autoSave,
36
+ onConflict,
37
+ onError,
38
+ onSubmit,
39
+ onSuccess,
40
+ resetOnSuccess,
41
+ schema,
42
+ values
43
+ }: {
44
+ autoSave?: {
45
+ debounceMs: number;
46
+ enabled: boolean;
47
+ };
48
+ onConflict?: (data: ConflictData) => void;
49
+ onError?: (e: unknown) => void;
50
+ onSubmit: (d: output<S>, force?: boolean) => output<S> | Promise<output<S> | undefined> | undefined;
51
+ onSuccess?: () => void;
52
+ resetOnSuccess?: boolean;
53
+ schema: S;
54
+ values?: output<S>;
55
+ }) => {
56
+ conflict: ConflictData | null;
57
+ error: Error | null;
58
+ instance: Api<output<S>>;
59
+ isDirty: boolean;
60
+ isPending: boolean;
61
+ lastSaved: number | null;
62
+ meta: FieldMetaMap;
63
+ reset: (vals?: output<S>) => void;
64
+ resolveConflict: (action: "cancel" | "overwrite" | "reload") => void;
65
+ schema: S;
66
+ watch: <K extends keyof output<S>>(name: K) => output<S>[K];
67
+ }, useFormMutation: <S extends ZodObject<ZodRawShape>>({
68
+ mutation: mutationRef,
69
+ onConflict,
70
+ onError,
71
+ onSuccess,
72
+ resetOnSuccess,
73
+ schema,
74
+ transform,
75
+ values
76
+ }: {
77
+ mutation: FunctionReference<"mutation">;
78
+ onConflict?: (data: ConflictData) => void;
79
+ onError?: (e: unknown) => void;
80
+ onSuccess?: () => void;
81
+ resetOnSuccess?: boolean;
82
+ schema: S;
83
+ transform?: (d: output<S>) => Record<string, unknown>;
84
+ values?: output<S>;
85
+ }) => {
86
+ conflict: ConflictData | null;
87
+ error: Error | null;
88
+ instance: Api<output<S>>;
89
+ isDirty: boolean;
90
+ isPending: boolean;
91
+ lastSaved: number | null;
92
+ meta: FieldMetaMap;
93
+ reset: (vals?: output<S> | undefined) => void;
94
+ resolveConflict: (action: "cancel" | "overwrite" | "reload") => void;
95
+ schema: S;
96
+ watch: <K extends keyof output<S>>(name: K) => output<S>[K];
97
+ };
98
+ //#endregion
99
+ export { useFormMutation as a, FieldMetaMap as c, useForm$1 as i, buildMeta as l, ConflictData as n, FieldKind as o, FormReturn as r, FieldMeta as s, Api as t, getMeta as u };