@squaredr/fieldcraft-supabase 1.0.0 → 1.1.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.
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,91 @@ 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
+ }
169
250
  // Annotate the CommonJS export names for ESM import in node:
170
251
  0 && (module.exports = {
171
252
  createSupabaseAdapter,
172
253
  createSupabaseDraftAdapter,
254
+ createSupabaseSchemaAdapter,
173
255
  decryptFields,
174
256
  encryptFields
175
257
  });
package/dist/index.mjs CHANGED
@@ -127,9 +127,90 @@ 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
+ }
130
210
  export {
131
211
  createSupabaseAdapter,
132
212
  createSupabaseDraftAdapter,
213
+ createSupabaseSchemaAdapter,
133
214
  decryptFields,
134
215
  encryptFields
135
216
  };
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.0",
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",
@@ -21,7 +21,7 @@
21
21
  "clean": "rm -rf dist"
22
22
  },
23
23
  "dependencies": {
24
- "@squaredr/fieldcraft-core": "^1.0.0"
24
+ "@squaredr/fieldcraft-core": "^1.2.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@supabase/supabase-js": "^2.0"