@squaredr/fieldcraft-supabase 1.0.0 → 1.1.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 CHANGED
@@ -84,6 +84,14 @@ const draftAdapter = createSupabaseDraftAdapter({
84
84
 
85
85
  The adapter is compatible with Supabase RLS policies. Configure your table's RLS rules to control which users can read/write responses.
86
86
 
87
+ ## Community
88
+
89
+ [![Discord](https://img.shields.io/discord/YOUR_SERVER_ID?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/YOUR_INVITE_LINK)
90
+
91
+ - [Discord](https://discord.gg/YOUR_INVITE_LINK) — Get help, share projects, request features
92
+ - [Docs](https://squaredr.tech/products/fieldcraft/docs/adapters) — Adapter documentation
93
+ - [Pro Tools](https://squaredr.tech/products/fieldcraft/admin-pro) — Visual FormBuilder, SchemaEditor, ResponseViewer, ThemeEditor
94
+
87
95
  ## License
88
96
 
89
97
  MIT
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { FormResponse, SubmitAdapter, DraftAdapter } from '@squaredr/fieldcraft-core';
1
+ import { FormResponse, SubmitAdapter, DraftAdapter, SchemaAdapter } from '@squaredr/fieldcraft-core';
2
2
 
3
3
  type SupabaseAdapterConfig = {
4
4
  /** Supabase client instance from @supabase/supabase-js */
@@ -34,9 +34,22 @@ type SupabaseQueryBuilder = {
34
34
  }): SupabaseFilterBuilder;
35
35
  delete(): SupabaseFilterBuilder;
36
36
  };
37
+ type SupabaseSchemaAdapterConfig = {
38
+ /** Supabase client instance */
39
+ client: SupabaseClient;
40
+ /** Table name for schemas (default: "formengine_schemas") */
41
+ table?: string;
42
+ /** Called on error */
43
+ onError?: (error: Error) => void;
44
+ };
37
45
  type SupabaseFilterBuilder = {
38
46
  eq(column: string, value: unknown): SupabaseFilterBuilder;
39
47
  gt(column: string, value: unknown): SupabaseFilterBuilder;
48
+ ilike(column: string, pattern: string): SupabaseFilterBuilder;
49
+ order(column: string, options?: {
50
+ ascending?: boolean;
51
+ }): SupabaseFilterBuilder;
52
+ range(from: number, to: number): SupabaseFilterBuilder;
40
53
  single(): Promise<{
41
54
  data: Record<string, unknown> | null;
42
55
  error: Error | null;
@@ -54,9 +67,28 @@ declare function createSupabaseAdapter(config: SupabaseAdapterConfig): SubmitAda
54
67
  /** Create a Supabase draft adapter. */
55
68
  declare function createSupabaseDraftAdapter(config: SupabaseDraftAdapterConfig): DraftAdapter;
56
69
 
70
+ /**
71
+ * Create a Supabase schema adapter for persisting FormEngineSchema documents.
72
+ *
73
+ * Requires a `formengine_schemas` table (or custom name) with the following columns:
74
+ *
75
+ * ```sql
76
+ * create table formengine_schemas (
77
+ * id text primary key,
78
+ * title text not null default '',
79
+ * version text not null default '1.0.0',
80
+ * status text not null default 'draft',
81
+ * definition jsonb not null,
82
+ * created_at timestamptz not null default now(),
83
+ * updated_at timestamptz not null default now()
84
+ * );
85
+ * ```
86
+ */
87
+ declare function createSupabaseSchemaAdapter(config: SupabaseSchemaAdapterConfig): SchemaAdapter;
88
+
57
89
  /** Encrypt specified fields in a values object. */
58
90
  declare function encryptFields(values: Record<string, unknown>, fieldIds: string[], keyBase64: string): Record<string, unknown>;
59
91
  /** Decrypt specified fields in a values object. */
60
92
  declare function decryptFields(values: Record<string, unknown>, fieldIds: string[], keyBase64: string): Record<string, unknown>;
61
93
 
62
- export { type SupabaseAdapterConfig, type SupabaseClient, type SupabaseDraftAdapterConfig, createSupabaseAdapter, createSupabaseDraftAdapter, decryptFields, encryptFields };
94
+ export { type SupabaseAdapterConfig, type SupabaseClient, type SupabaseDraftAdapterConfig, type SupabaseSchemaAdapterConfig, createSupabaseAdapter, createSupabaseDraftAdapter, createSupabaseSchemaAdapter, decryptFields, encryptFields };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FormResponse, SubmitAdapter, DraftAdapter } from '@squaredr/fieldcraft-core';
1
+ import { FormResponse, SubmitAdapter, DraftAdapter, SchemaAdapter } from '@squaredr/fieldcraft-core';
2
2
 
3
3
  type SupabaseAdapterConfig = {
4
4
  /** Supabase client instance from @supabase/supabase-js */
@@ -34,9 +34,22 @@ type SupabaseQueryBuilder = {
34
34
  }): SupabaseFilterBuilder;
35
35
  delete(): SupabaseFilterBuilder;
36
36
  };
37
+ type SupabaseSchemaAdapterConfig = {
38
+ /** Supabase client instance */
39
+ client: SupabaseClient;
40
+ /** Table name for schemas (default: "formengine_schemas") */
41
+ table?: string;
42
+ /** Called on error */
43
+ onError?: (error: Error) => void;
44
+ };
37
45
  type SupabaseFilterBuilder = {
38
46
  eq(column: string, value: unknown): SupabaseFilterBuilder;
39
47
  gt(column: string, value: unknown): SupabaseFilterBuilder;
48
+ ilike(column: string, pattern: string): SupabaseFilterBuilder;
49
+ order(column: string, options?: {
50
+ ascending?: boolean;
51
+ }): SupabaseFilterBuilder;
52
+ range(from: number, to: number): SupabaseFilterBuilder;
40
53
  single(): Promise<{
41
54
  data: Record<string, unknown> | null;
42
55
  error: Error | null;
@@ -54,9 +67,28 @@ declare function createSupabaseAdapter(config: SupabaseAdapterConfig): SubmitAda
54
67
  /** Create a Supabase draft adapter. */
55
68
  declare function createSupabaseDraftAdapter(config: SupabaseDraftAdapterConfig): DraftAdapter;
56
69
 
70
+ /**
71
+ * Create a Supabase schema adapter for persisting FormEngineSchema documents.
72
+ *
73
+ * Requires a `formengine_schemas` table (or custom name) with the following columns:
74
+ *
75
+ * ```sql
76
+ * create table formengine_schemas (
77
+ * id text primary key,
78
+ * title text not null default '',
79
+ * version text not null default '1.0.0',
80
+ * status text not null default 'draft',
81
+ * definition jsonb not null,
82
+ * created_at timestamptz not null default now(),
83
+ * updated_at timestamptz not null default now()
84
+ * );
85
+ * ```
86
+ */
87
+ declare function createSupabaseSchemaAdapter(config: SupabaseSchemaAdapterConfig): SchemaAdapter;
88
+
57
89
  /** Encrypt specified fields in a values object. */
58
90
  declare function encryptFields(values: Record<string, unknown>, fieldIds: string[], keyBase64: string): Record<string, unknown>;
59
91
  /** Decrypt specified fields in a values object. */
60
92
  declare function decryptFields(values: Record<string, unknown>, fieldIds: string[], keyBase64: string): Record<string, unknown>;
61
93
 
62
- export { type SupabaseAdapterConfig, type SupabaseClient, type SupabaseDraftAdapterConfig, createSupabaseAdapter, createSupabaseDraftAdapter, decryptFields, encryptFields };
94
+ export { type SupabaseAdapterConfig, type SupabaseClient, type SupabaseDraftAdapterConfig, type SupabaseSchemaAdapterConfig, createSupabaseAdapter, createSupabaseDraftAdapter, createSupabaseSchemaAdapter, decryptFields, encryptFields };
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  createSupabaseAdapter: () => createSupabaseAdapter,
34
34
  createSupabaseDraftAdapter: () => createSupabaseDraftAdapter,
35
+ createSupabaseSchemaAdapter: () => createSupabaseSchemaAdapter,
35
36
  decryptFields: () => decryptFields,
36
37
  encryptFields: () => encryptFields
37
38
  });
@@ -166,10 +167,109 @@ function createSupabaseDraftAdapter(config) {
166
167
  }
167
168
  };
168
169
  }
170
+
171
+ // src/supabase-schema-adapter.ts
172
+ function createSupabaseSchemaAdapter(config) {
173
+ const tableName = config.table ?? "formengine_schemas";
174
+ return {
175
+ name: "supabase-schema",
176
+ async save(schema) {
177
+ const { error } = await config.client.from(tableName).upsert(
178
+ {
179
+ id: schema.id,
180
+ title: schema.title ?? "",
181
+ version: schema.version ?? "1.0.0",
182
+ status: "draft",
183
+ definition: schema,
184
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
185
+ },
186
+ { onConflict: "id" }
187
+ );
188
+ if (error) {
189
+ const err = new Error(`Supabase schema save failed: ${error.message}`);
190
+ config.onError?.(err);
191
+ throw err;
192
+ }
193
+ },
194
+ async load(schemaId) {
195
+ const { data, error } = await config.client.from(tableName).select("definition").eq("id", schemaId).single();
196
+ if (error || !data) return null;
197
+ return data.definition;
198
+ },
199
+ async delete(schemaId) {
200
+ const { error } = await config.client.from(tableName).delete().eq("id", schemaId);
201
+ if (error) {
202
+ const err = new Error(`Supabase schema delete failed: ${error.message}`);
203
+ config.onError?.(err);
204
+ throw err;
205
+ }
206
+ },
207
+ async list(params) {
208
+ const page = params?.page ?? 1;
209
+ const pageSize = params?.pageSize ?? 20;
210
+ const from = (page - 1) * pageSize;
211
+ const to = from + pageSize - 1;
212
+ let query = config.client.from(tableName).select("id, title, version, status, created_at, updated_at");
213
+ if (params?.search) {
214
+ query = query.ilike("title", `%${params.search}%`);
215
+ }
216
+ if (params?.status) {
217
+ query = query.eq("status", params.status);
218
+ }
219
+ const sortBy = params?.sortBy ?? "updatedAt";
220
+ const sortOrder = params?.sortOrder ?? "desc";
221
+ const columnMap = {
222
+ title: "title",
223
+ updatedAt: "updated_at",
224
+ createdAt: "created_at"
225
+ };
226
+ const sortColumn = columnMap[sortBy] ?? "updated_at";
227
+ query = query.order(sortColumn, { ascending: sortOrder === "asc" }).range(from, to);
228
+ const { data, error } = await query.then((res) => res);
229
+ if (error || !data) {
230
+ return { items: [], total: 0, page, pageSize };
231
+ }
232
+ const items = data.map((row) => ({
233
+ id: row.id,
234
+ title: row.title,
235
+ version: row.version,
236
+ updatedAt: row.updated_at,
237
+ createdAt: row.created_at,
238
+ status: row.status
239
+ }));
240
+ return {
241
+ items,
242
+ total: items.length < pageSize ? from + items.length : from + pageSize + 1,
243
+ page,
244
+ pageSize
245
+ };
246
+ },
247
+ onError: config.onError
248
+ };
249
+ }
250
+
251
+ // src/index.ts
252
+ if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
253
+ const _fc_banner = `
254
+ %c FieldCraft Supabase %c v1.1.1
255
+
256
+ %cField-level encryption \xB7 RLS \xB7 Schema CRUD
257
+
258
+ Docs \u2192 https://squaredr.tech/products/fieldcraft/docs/adapters
259
+ Discord \u2192 https://discord.gg/YOUR_INVITE_LINK
260
+ `;
261
+ console.log(
262
+ _fc_banner,
263
+ "background:#2563eb;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px",
264
+ "background:#1e40af;color:#fff;padding:2px 6px;border-radius:0 3px 3px 0",
265
+ "color:#6b7280"
266
+ );
267
+ }
169
268
  // Annotate the CommonJS export names for ESM import in node:
170
269
  0 && (module.exports = {
171
270
  createSupabaseAdapter,
172
271
  createSupabaseDraftAdapter,
272
+ createSupabaseSchemaAdapter,
173
273
  decryptFields,
174
274
  encryptFields
175
275
  });
package/dist/index.mjs CHANGED
@@ -127,9 +127,108 @@ function createSupabaseDraftAdapter(config) {
127
127
  }
128
128
  };
129
129
  }
130
+
131
+ // src/supabase-schema-adapter.ts
132
+ function createSupabaseSchemaAdapter(config) {
133
+ const tableName = config.table ?? "formengine_schemas";
134
+ return {
135
+ name: "supabase-schema",
136
+ async save(schema) {
137
+ const { error } = await config.client.from(tableName).upsert(
138
+ {
139
+ id: schema.id,
140
+ title: schema.title ?? "",
141
+ version: schema.version ?? "1.0.0",
142
+ status: "draft",
143
+ definition: schema,
144
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
145
+ },
146
+ { onConflict: "id" }
147
+ );
148
+ if (error) {
149
+ const err = new Error(`Supabase schema save failed: ${error.message}`);
150
+ config.onError?.(err);
151
+ throw err;
152
+ }
153
+ },
154
+ async load(schemaId) {
155
+ const { data, error } = await config.client.from(tableName).select("definition").eq("id", schemaId).single();
156
+ if (error || !data) return null;
157
+ return data.definition;
158
+ },
159
+ async delete(schemaId) {
160
+ const { error } = await config.client.from(tableName).delete().eq("id", schemaId);
161
+ if (error) {
162
+ const err = new Error(`Supabase schema delete failed: ${error.message}`);
163
+ config.onError?.(err);
164
+ throw err;
165
+ }
166
+ },
167
+ async list(params) {
168
+ const page = params?.page ?? 1;
169
+ const pageSize = params?.pageSize ?? 20;
170
+ const from = (page - 1) * pageSize;
171
+ const to = from + pageSize - 1;
172
+ let query = config.client.from(tableName).select("id, title, version, status, created_at, updated_at");
173
+ if (params?.search) {
174
+ query = query.ilike("title", `%${params.search}%`);
175
+ }
176
+ if (params?.status) {
177
+ query = query.eq("status", params.status);
178
+ }
179
+ const sortBy = params?.sortBy ?? "updatedAt";
180
+ const sortOrder = params?.sortOrder ?? "desc";
181
+ const columnMap = {
182
+ title: "title",
183
+ updatedAt: "updated_at",
184
+ createdAt: "created_at"
185
+ };
186
+ const sortColumn = columnMap[sortBy] ?? "updated_at";
187
+ query = query.order(sortColumn, { ascending: sortOrder === "asc" }).range(from, to);
188
+ const { data, error } = await query.then((res) => res);
189
+ if (error || !data) {
190
+ return { items: [], total: 0, page, pageSize };
191
+ }
192
+ const items = data.map((row) => ({
193
+ id: row.id,
194
+ title: row.title,
195
+ version: row.version,
196
+ updatedAt: row.updated_at,
197
+ createdAt: row.created_at,
198
+ status: row.status
199
+ }));
200
+ return {
201
+ items,
202
+ total: items.length < pageSize ? from + items.length : from + pageSize + 1,
203
+ page,
204
+ pageSize
205
+ };
206
+ },
207
+ onError: config.onError
208
+ };
209
+ }
210
+
211
+ // src/index.ts
212
+ if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
213
+ const _fc_banner = `
214
+ %c FieldCraft Supabase %c v1.1.1
215
+
216
+ %cField-level encryption \xB7 RLS \xB7 Schema CRUD
217
+
218
+ Docs \u2192 https://squaredr.tech/products/fieldcraft/docs/adapters
219
+ Discord \u2192 https://discord.gg/YOUR_INVITE_LINK
220
+ `;
221
+ console.log(
222
+ _fc_banner,
223
+ "background:#2563eb;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px",
224
+ "background:#1e40af;color:#fff;padding:2px 6px;border-radius:0 3px 3px 0",
225
+ "color:#6b7280"
226
+ );
227
+ }
130
228
  export {
131
229
  createSupabaseAdapter,
132
230
  createSupabaseDraftAdapter,
231
+ createSupabaseSchemaAdapter,
133
232
  decryptFields,
134
233
  encryptFields
135
234
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squaredr/fieldcraft-supabase",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "FieldCraft Supabase storage adapter with field-level AES-256-GCM encryption",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -12,16 +12,13 @@
12
12
  "require": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": ["dist", "LICENSE"],
15
+ "files": [
16
+ "dist",
17
+ "LICENSE"
18
+ ],
16
19
  "sideEffects": false,
17
- "scripts": {
18
- "build": "tsup src/index.ts --format cjs,esm --dts",
19
- "test": "vitest run",
20
- "typecheck": "tsc --noEmit",
21
- "clean": "rm -rf dist"
22
- },
23
20
  "dependencies": {
24
- "@squaredr/fieldcraft-core": "^1.0.0"
21
+ "@squaredr/fieldcraft-core": "^1.2.0"
25
22
  },
26
23
  "peerDependencies": {
27
24
  "@supabase/supabase-js": "^2.0"
@@ -43,8 +40,21 @@
43
40
  "url": "git+https://github.com/SquaredR98/fieldcraft.git",
44
41
  "directory": "packages/adapters/supabase"
45
42
  },
46
- "keywords": ["fieldcraft", "form-engine", "supabase", "adapter", "storage", "encryption"],
43
+ "keywords": [
44
+ "fieldcraft",
45
+ "form-engine",
46
+ "supabase",
47
+ "adapter",
48
+ "storage",
49
+ "encryption"
50
+ ],
47
51
  "engines": {
48
52
  "node": ">=18"
53
+ },
54
+ "scripts": {
55
+ "build": "tsup src/index.ts --format cjs,esm --dts",
56
+ "test": "vitest run",
57
+ "typecheck": "tsc --noEmit",
58
+ "clean": "rm -rf dist"
49
59
  }
50
- }
60
+ }