@gallopsystems/agent-skills 1.19.0 → 1.20.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/package.json
CHANGED
|
@@ -49,6 +49,39 @@ Rule of thumb: **convert when the raw SQL names a column or table; leave it when
|
|
|
49
49
|
it is only parameters and literals.** The latter compiles to identical SQL and the
|
|
50
50
|
"type-safe" rewrite verifies nothing.
|
|
51
51
|
|
|
52
|
+
**One more trap in the canonical `count(*)` swap above: it returns a *string* at
|
|
53
|
+
runtime, and a type parameter won't change that.** `eb.fn.countAll()` emits a bare
|
|
54
|
+
`count(*)`, which PostgreSQL returns as `bigint` — and the `pg` driver deserializes
|
|
55
|
+
`bigint` to a JavaScript **string** by default (see why below). So the row's `count`
|
|
56
|
+
is `"2"`, not `2`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// MISLEADING - compiles, but lies about the runtime value
|
|
60
|
+
.select((eb) => eb.fn.countAll<number>().as("count"))
|
|
61
|
+
// `<number>` is a pure type assertion. The driver still hands back a string,
|
|
62
|
+
// so `row.count === 2` is false and `row.count.toFixed()` throws.
|
|
63
|
+
|
|
64
|
+
// RIGHT - cast in SQL so the driver parses a real number
|
|
65
|
+
.select((eb) => eb.cast<number>(eb.fn.countAll(), "integer").as("count"))
|
|
66
|
+
// emits `cast(count(*) as integer)` -> int4 -> the driver yields a JS number
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The rule generalizes: **typing a `sql`/builder expression as `<number>` does not
|
|
70
|
+
change the value the driver produces — only a SQL-level cast does.** Any
|
|
71
|
+
`bigint`-returning aggregate (`count`, `sum` over a `bigint`, etc.) needs an explicit
|
|
72
|
+
`cast` when downstream code expects a JS number.
|
|
73
|
+
|
|
74
|
+
> **Why `bigint` comes back as a string — and don't "fix" it globally.** The `pg`
|
|
75
|
+
> driver returns `int8`/`bigint` as a string on purpose: a JS `number` is a float64,
|
|
76
|
+
> exact only to `2^53 - 1`, while `bigint` runs to `2^63 - 1`, so auto-parsing would
|
|
77
|
+
> silently corrupt large IDs. You *can* register `pg.types.setTypeParser` to coerce
|
|
78
|
+
> `int8` to a number, but it reintroduces that precision footgun for **every**
|
|
79
|
+
> `bigint` column (primary keys included), and `kysely-codegen` has no
|
|
80
|
+
> `--bigint-parser` flag (only `--date-parser` / `--numeric-parser`) — so the
|
|
81
|
+
> generated `Int8` type still selects as `string` and you would be hand-overriding an
|
|
82
|
+
> alias that regeneration clobbers. Keep the string default and `cast` per query where
|
|
83
|
+
> you need a number.
|
|
84
|
+
|
|
52
85
|
### 2. Don't Forget .execute()
|
|
53
86
|
|
|
54
87
|
Queries are lazy - they won't run without calling an execute method:
|
|
@@ -351,6 +351,37 @@ await db
|
|
|
351
351
|
.execute();
|
|
352
352
|
```
|
|
353
353
|
|
|
354
|
+
**`doUpdateSet` object vs callback form.** `doUpdateSet` takes either a plain
|
|
355
|
+
object `doUpdateSet({ col: value })` or a callback `doUpdateSet((eb) => ({ ... }))`.
|
|
356
|
+
The object form has no expression builder, so the moment you need a *builder
|
|
357
|
+
expression* for any column — arithmetic, an `EXCLUDED` reference, a coalesce — you
|
|
358
|
+
must switch the whole call to the callback form. Inside it, a **bare column name
|
|
359
|
+
refers to the target (existing) row**, and **`eb.ref("excluded.<col>")` references
|
|
360
|
+
the incoming row** (Kysely lowercases it to Postgres's `excluded` pseudo-table).
|
|
361
|
+
The other columns come along unchanged inside the returned object:
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
// Raw sql for the increment / EXCLUDED — column names are unchecked strings
|
|
365
|
+
.onConflict((oc) =>
|
|
366
|
+
oc.column("email").doUpdateSet({
|
|
367
|
+
reply_count: sql`reply_count + 1`,
|
|
368
|
+
email: sql`EXCLUDED.email`,
|
|
369
|
+
status: "active", // plain value, fine in either form
|
|
370
|
+
}),
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
// Callback form unlocks eb(): both column refs are now type-checked
|
|
374
|
+
.onConflict((oc) =>
|
|
375
|
+
oc.column("email").doUpdateSet((eb) => ({
|
|
376
|
+
reply_count: eb("reply_count", "+", 1),
|
|
377
|
+
email: eb.ref("excluded.email"),
|
|
378
|
+
status: "active",
|
|
379
|
+
})),
|
|
380
|
+
)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The same object-vs-callback rule applies to `.set()` on a plain `UPDATE` (see below).
|
|
384
|
+
|
|
354
385
|
### UPDATE Operations
|
|
355
386
|
|
|
356
387
|
```typescript
|