@kahitsan/plugin-sdk 0.5.0 → 0.5.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/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/dev/sqlite-pool.ts +24 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kahitsan/plugin-sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "THE single public author surface for Hilinga plugins: createPlugin, auth guards, the workspace-scoped data surface (makeDataSurface) + migrations + withTenantContext, cross-plugin RPC, and the wire-protocol verify surface. One npm package, no -composite/-protocol companions. Self-contained, no kernel internals, no signing capability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/dev/sqlite-pool.ts
CHANGED
|
@@ -182,6 +182,21 @@ function quote(name: string): string {
|
|
|
182
182
|
return `"${name.replace(/"/g, '""')}"`;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/** For a degraded bare-aggregate read (no GROUP BY), return a single zero row keyed by
|
|
186
|
+
* the projected column name so `rows[0].count` resolves to 0 instead of throwing.
|
|
187
|
+
* Returns null when the SQL isn't a bare aggregate (caller then degrades to []). */
|
|
188
|
+
function aggregateZeroRow<R>(sql: string): QueryResult<R> | null {
|
|
189
|
+
if (/\bGROUP\s+BY\b/i.test(sql)) return null;
|
|
190
|
+
const m = /^\s*SELECT\s+(.+?)\s+FROM\b/is.exec(sql);
|
|
191
|
+
if (!m) return null;
|
|
192
|
+
const proj = m[1].trim();
|
|
193
|
+
const fn = /^(count|sum|avg|min|max|coalesce)\s*\(/i.exec(proj);
|
|
194
|
+
if (!fn) return null;
|
|
195
|
+
const aliasM = /\bAS\s+"?([A-Za-z_][A-Za-z0-9_]*)"?\s*$/i.exec(proj);
|
|
196
|
+
const alias = aliasM ? aliasM[1] : fn[1].toLowerCase();
|
|
197
|
+
return { rows: [{ [alias]: 0 } as unknown as R], rowCount: 1 };
|
|
198
|
+
}
|
|
199
|
+
|
|
185
200
|
/** Classify a SQLite execution error into a tolerable empty result (Vision §5.1
|
|
186
201
|
* resilience), or null to rethrow. Tolerated: an idempotent re-`ADD COLUMN`; any
|
|
187
202
|
* statement during the boot migration window or any DDL (best-effort — the plugin
|
|
@@ -198,7 +213,15 @@ function tolerateError<R>(sql: string, err: Error): QueryResult<R> | null {
|
|
|
198
213
|
if (/duplicate column name/i.test(msg) && /\bADD\s+COLUMN\b/i.test(sql)) return empty;
|
|
199
214
|
const migrating = (globalThis as { __KSERP_DEV_MIGRATING__?: boolean }).__KSERP_DEV_MIGRATING__;
|
|
200
215
|
if (migrating || IS_DDL.test(sql)) return (debug("skipped a Postgres-only statement"), empty);
|
|
201
|
-
if (RETURNS_ROWS.test(sql))
|
|
216
|
+
if (RETURNS_ROWS.test(sql)) {
|
|
217
|
+
// A bare aggregate SELECT (COUNT/SUM/… with no GROUP BY) ALWAYS returns exactly one
|
|
218
|
+
// row in Postgres (0/NULL over an empty set). Degrading it to [] crashes the common
|
|
219
|
+
// list-count handler `rows[0].count`, so hand back a single zero row keyed by the
|
|
220
|
+
// projected column (explicit `AS alias`, else pg's default = the function name).
|
|
221
|
+
const agg = aggregateZeroRow<R>(sql);
|
|
222
|
+
if (agg) return (debug("degraded a Postgres-only aggregate (zero row)"), agg);
|
|
223
|
+
return (debug("degraded a Postgres-only read (empty result)"), empty);
|
|
224
|
+
}
|
|
202
225
|
if (process.env.KSERP_DEV_SQL_DEBUG)
|
|
203
226
|
console.error(`[dev-stub sqlite] FAILED SQL:\n${sql}\n→ ${msg}`);
|
|
204
227
|
return null;
|