@secondlayer/subgraphs 3.19.9 → 3.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/dist/src/index.d.ts +39 -2
- package/dist/src/index.js +105 -12
- package/dist/src/index.js.map +3 -3
- package/dist/src/schema/index.d.ts +26 -0
- package/dist/src/schema/index.js +105 -12
- package/dist/src/schema/index.js.map +3 -3
- package/package.json +1 -1
package/dist/src/index.d.ts
CHANGED
|
@@ -411,7 +411,16 @@ type AggregateResult<
|
|
|
411
411
|
max: Record<ColsOf<A, "max">, string | null>
|
|
412
412
|
} : unknown);
|
|
413
413
|
interface SubgraphTableClient<TRow> {
|
|
414
|
-
|
|
414
|
+
/**
|
|
415
|
+
* List rows. `fields` narrows BOTH the wire payload and the return type:
|
|
416
|
+
* the server projects the SELECT to exactly those columns, so an
|
|
417
|
+
* unrequested field is physically absent — reading it is a compile error,
|
|
418
|
+
* not `undefined`. The `const` type parameter keeps the literal field
|
|
419
|
+
* names without `as const` at the call site (same pattern as `aggregate`).
|
|
420
|
+
*/
|
|
421
|
+
findMany<const F extends readonly (keyof TRow & string)[] = readonly (keyof TRow & string)[]>(options?: Omit<FindManyOptions<TRow>, "fields"> & {
|
|
422
|
+
fields?: F
|
|
423
|
+
}): Promise<Pick<TRow, F[number]>[]>;
|
|
415
424
|
count(where?: WhereInput<TRow> & SystemWhereAliases): Promise<number>;
|
|
416
425
|
/**
|
|
417
426
|
* Scalar aggregates over the filtered set. SUM/MIN/MAX accept numeric columns
|
|
@@ -793,19 +802,41 @@ interface TableDiff {
|
|
|
793
802
|
removedTables: string[];
|
|
794
803
|
/** Per-table column diffs (only for tables present in both) */
|
|
795
804
|
tables: Record<string, ColumnDiff>;
|
|
805
|
+
/** Per-table uniqueKeys / composite-index diffs (tables present in both) */
|
|
806
|
+
constraints: Record<string, ConstraintDiff>;
|
|
796
807
|
}
|
|
797
808
|
interface ColumnDiff {
|
|
798
809
|
added: string[];
|
|
799
810
|
removed: string[];
|
|
811
|
+
/** Data-shape changes (type/nullable/default) — breaking. */
|
|
800
812
|
changed: string[];
|
|
813
|
+
/** Only the `indexed`/`search` flags differ — index DDL, never a rebuild. */
|
|
814
|
+
indexChanged: string[];
|
|
815
|
+
}
|
|
816
|
+
interface ConstraintDiff {
|
|
817
|
+
/** New uniqueKeys entries → `ALTER TABLE … ADD CONSTRAINT` (additive). */
|
|
818
|
+
addedUniqueKeys: string[][];
|
|
819
|
+
/** Removed uniqueKeys entries — breaking: handler upserts may target them. */
|
|
820
|
+
removedUniqueKeys: string[][];
|
|
821
|
+
/** New composite `indexes` entries → CREATE INDEX (additive). */
|
|
822
|
+
addedIndexes: string[][];
|
|
823
|
+
/** Removed composite `indexes` entries → DROP INDEX (additive). */
|
|
824
|
+
removedIndexes: string[][];
|
|
801
825
|
}
|
|
802
826
|
/**
|
|
803
827
|
* Compare two multi-table subgraph schemas and return differences.
|
|
828
|
+
*
|
|
829
|
+
* Index-only changes (`indexed`/`search` flags, composite `indexes`) are
|
|
830
|
+
* reported separately from data-shape changes: an index never requires a
|
|
831
|
+
* rebuild, so it must never land in `changed`/breaking — flipping
|
|
832
|
+
* `indexed: true` on a populated column used to cost a full reindex.
|
|
804
833
|
*/
|
|
805
834
|
declare function diffSchema(existing: SubgraphSchema, incoming: SubgraphSchema): TableDiff;
|
|
806
835
|
/**
|
|
807
836
|
* Returns true if the diff contains any breaking changes
|
|
808
|
-
* (removed tables, removed columns,
|
|
837
|
+
* (removed tables, removed columns, changed column types, or removed
|
|
838
|
+
* uniqueKeys — handler upserts `ON CONFLICT` against those).
|
|
839
|
+
* Index-flag and composite-index changes are never breaking.
|
|
809
840
|
*/
|
|
810
841
|
declare function hasBreakingChanges(diff: TableDiff): {
|
|
811
842
|
breaking: boolean
|
|
@@ -816,6 +847,10 @@ interface DeployDiff {
|
|
|
816
847
|
removedTables: string[];
|
|
817
848
|
addedColumns: Record<string, string[]>;
|
|
818
849
|
breakingChanges: string[];
|
|
850
|
+
/** Columns whose only change was the `indexed`/`search` flag (index DDL applied in place). */
|
|
851
|
+
indexChanges?: Record<string, string[]>;
|
|
852
|
+
/** uniqueKeys added in place via ALTER TABLE … ADD CONSTRAINT. */
|
|
853
|
+
addedUniqueKeys?: Record<string, string[][]>;
|
|
819
854
|
}
|
|
820
855
|
interface ByoMigrationPlan {
|
|
821
856
|
schemaName: string;
|
|
@@ -877,6 +912,8 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
|
|
|
877
912
|
subgraphId: string
|
|
878
913
|
version: string
|
|
879
914
|
diff?: DeployDiff
|
|
915
|
+
/** Non-fatal caveats the caller should surface (e.g. handler grafts at tip). */
|
|
916
|
+
warnings?: string[]
|
|
880
917
|
}>;
|
|
881
918
|
/** A (decoded event type, optional contract scope) pair the sparse probe
|
|
882
919
|
* checks. Contract scoping is what makes token-subgraph reindexes leap over
|
package/dist/src/index.js
CHANGED
|
@@ -3843,31 +3843,53 @@ import { sql as sql4 } from "kysely";
|
|
|
3843
3843
|
function toJsonSafe(obj) {
|
|
3844
3844
|
return JSON.parse(JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value));
|
|
3845
3845
|
}
|
|
3846
|
+
function columnDataShape(col) {
|
|
3847
|
+
const { indexed, search, ...rest } = col ?? {};
|
|
3848
|
+
return JSON.stringify(rest, Object.keys(rest).sort());
|
|
3849
|
+
}
|
|
3850
|
+
function diffKeyLists(existing, incoming) {
|
|
3851
|
+
const key2 = (cols) => JSON.stringify(cols);
|
|
3852
|
+
const existingSet = new Set((existing ?? []).map(key2));
|
|
3853
|
+
const incomingSet = new Set((incoming ?? []).map(key2));
|
|
3854
|
+
return {
|
|
3855
|
+
added: (incoming ?? []).filter((cols) => !existingSet.has(key2(cols))),
|
|
3856
|
+
removed: (existing ?? []).filter((cols) => !incomingSet.has(key2(cols)))
|
|
3857
|
+
};
|
|
3858
|
+
}
|
|
3846
3859
|
function diffSchema(existing, incoming) {
|
|
3847
3860
|
const existingTables = new Set(Object.keys(existing));
|
|
3848
3861
|
const incomingTables = new Set(Object.keys(incoming));
|
|
3849
3862
|
const addedTables = [...incomingTables].filter((t) => !existingTables.has(t));
|
|
3850
3863
|
const removedTables = [...existingTables].filter((t) => !incomingTables.has(t));
|
|
3851
3864
|
const tables = {};
|
|
3865
|
+
const constraints = {};
|
|
3852
3866
|
for (const tableName of incomingTables) {
|
|
3853
3867
|
if (!existingTables.has(tableName))
|
|
3854
3868
|
continue;
|
|
3855
|
-
const
|
|
3856
|
-
const
|
|
3869
|
+
const existingTable = existing[tableName];
|
|
3870
|
+
const incomingTable = incoming[tableName];
|
|
3871
|
+
const existingCols = existingTable?.columns;
|
|
3872
|
+
const incomingCols = incomingTable?.columns;
|
|
3857
3873
|
const existingKeys = new Set(Object.keys(existingCols));
|
|
3858
3874
|
const incomingKeys = new Set(Object.keys(incomingCols));
|
|
3875
|
+
const common = [...incomingKeys].filter((k) => existingKeys.has(k));
|
|
3876
|
+
const sortedStringify = (o) => JSON.stringify(o, Object.keys(o).sort());
|
|
3859
3877
|
tables[tableName] = {
|
|
3860
3878
|
added: [...incomingKeys].filter((k) => !existingKeys.has(k)),
|
|
3861
3879
|
removed: [...existingKeys].filter((k) => !incomingKeys.has(k)),
|
|
3862
|
-
changed:
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3880
|
+
changed: common.filter((k) => columnDataShape(existingCols[k]) !== columnDataShape(incomingCols[k])),
|
|
3881
|
+
indexChanged: common.filter((k) => columnDataShape(existingCols[k]) === columnDataShape(incomingCols[k]) && sortedStringify(existingCols[k]) !== sortedStringify(incomingCols[k]))
|
|
3882
|
+
};
|
|
3883
|
+
const uq = diffKeyLists(existingTable?.uniqueKeys, incomingTable?.uniqueKeys);
|
|
3884
|
+
const ix = diffKeyLists(existingTable?.indexes, incomingTable?.indexes);
|
|
3885
|
+
constraints[tableName] = {
|
|
3886
|
+
addedUniqueKeys: uq.added,
|
|
3887
|
+
removedUniqueKeys: uq.removed,
|
|
3888
|
+
addedIndexes: ix.added,
|
|
3889
|
+
removedIndexes: ix.removed
|
|
3868
3890
|
};
|
|
3869
3891
|
}
|
|
3870
|
-
return { addedTables, removedTables, tables };
|
|
3892
|
+
return { addedTables, removedTables, tables, constraints };
|
|
3871
3893
|
}
|
|
3872
3894
|
function hasBreakingChanges(diff) {
|
|
3873
3895
|
const reasons = [];
|
|
@@ -3882,6 +3904,11 @@ function hasBreakingChanges(diff) {
|
|
|
3882
3904
|
reasons.push(`${table}: changed columns [${colDiff.changed.join(", ")}]`);
|
|
3883
3905
|
}
|
|
3884
3906
|
}
|
|
3907
|
+
for (const [table, conDiff] of Object.entries(diff.constraints ?? {})) {
|
|
3908
|
+
if (conDiff.removedUniqueKeys.length > 0) {
|
|
3909
|
+
reasons.push(`${table}: removed uniqueKeys [${conDiff.removedUniqueKeys.map((cols) => `(${cols.join(", ")})`).join(", ")}]`);
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3885
3912
|
return { breaking: reasons.length > 0, reasons };
|
|
3886
3913
|
}
|
|
3887
3914
|
function bumpPatch(version) {
|
|
@@ -3987,7 +4014,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
3987
4014
|
return {
|
|
3988
4015
|
action: handlerChanged ? "handler_updated" : "unchanged",
|
|
3989
4016
|
subgraphId: existing.id,
|
|
3990
|
-
version: existing.version
|
|
4017
|
+
version: existing.version,
|
|
4018
|
+
...handlerChanged ? {
|
|
4019
|
+
warnings: [
|
|
4020
|
+
"handler changed but schema did not: new logic applies from the current tip only. Rows already indexed were computed by the previous handler. To recompute history, redeploy with --reindex (or backfill a range)."
|
|
4021
|
+
]
|
|
4022
|
+
} : {}
|
|
3991
4023
|
};
|
|
3992
4024
|
}
|
|
3993
4025
|
if (existing.schema_hash === hash && opts?.forceReindex) {
|
|
@@ -4058,6 +4090,65 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4058
4090
|
}
|
|
4059
4091
|
}
|
|
4060
4092
|
}
|
|
4093
|
+
const existingSchema = existing.definition.schema;
|
|
4094
|
+
const flagFlips = {};
|
|
4095
|
+
const needsTrgmForFlips = Object.entries(diff.tables).some(([tableName, colDiff]) => colDiff.indexChanged.some((colName) => def.schema[tableName]?.columns[colName]?.search && !existingSchema[tableName]?.columns[colName]?.search));
|
|
4096
|
+
if (needsTrgmForFlips) {
|
|
4097
|
+
await sql4.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
|
|
4098
|
+
}
|
|
4099
|
+
for (const [tableName, colDiff] of Object.entries(diff.tables)) {
|
|
4100
|
+
if (colDiff.indexChanged.length === 0)
|
|
4101
|
+
continue;
|
|
4102
|
+
flagFlips[tableName] = colDiff.indexChanged;
|
|
4103
|
+
const qualifiedName = `${schemaName}.${tableName}`;
|
|
4104
|
+
for (const colName of colDiff.indexChanged) {
|
|
4105
|
+
const was = existingSchema[tableName]?.columns[colName];
|
|
4106
|
+
const now = def.schema[tableName]?.columns[colName];
|
|
4107
|
+
if (!now)
|
|
4108
|
+
continue;
|
|
4109
|
+
const plainIdx = `idx_${schemaName}_${tableName}_${colName}`;
|
|
4110
|
+
const trgmIdx = `idx_${schemaName}_${tableName}_${colName}_trgm`;
|
|
4111
|
+
if (now.indexed && !was?.indexed) {
|
|
4112
|
+
await sql4.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${plainIdx} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
4113
|
+
} else if (!now.indexed && was?.indexed) {
|
|
4114
|
+
await sql4.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${plainIdx}`).execute(ddlDb);
|
|
4115
|
+
}
|
|
4116
|
+
if (now.search && !was?.search) {
|
|
4117
|
+
await sql4.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${trgmIdx} ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
4118
|
+
} else if (!now.search && was?.search) {
|
|
4119
|
+
await sql4.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${trgmIdx}`).execute(ddlDb);
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
const addedUq = {};
|
|
4124
|
+
for (const [tableName, conDiff] of Object.entries(diff.constraints)) {
|
|
4125
|
+
const qualifiedName = `${schemaName}.${tableName}`;
|
|
4126
|
+
for (const cols of conDiff.addedUniqueKeys) {
|
|
4127
|
+
const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
|
|
4128
|
+
const exists = await sql4`
|
|
4129
|
+
SELECT EXISTS (
|
|
4130
|
+
SELECT 1 FROM pg_constraint
|
|
4131
|
+
WHERE conname = ${constraintName}
|
|
4132
|
+
) AS "exists"
|
|
4133
|
+
`.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
|
|
4134
|
+
if (!exists) {
|
|
4135
|
+
await sql4.raw(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`).execute(ddlDb);
|
|
4136
|
+
}
|
|
4137
|
+
addedUq[tableName] = [...addedUq[tableName] ?? [], cols];
|
|
4138
|
+
}
|
|
4139
|
+
for (const cols of conDiff.addedIndexes) {
|
|
4140
|
+
const i = (def.schema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
|
|
4141
|
+
const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
|
|
4142
|
+
await sql4.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`).execute(ddlDb);
|
|
4143
|
+
}
|
|
4144
|
+
for (const cols of conDiff.removedIndexes) {
|
|
4145
|
+
const i = (existingSchema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
|
|
4146
|
+
if (i < 0)
|
|
4147
|
+
continue;
|
|
4148
|
+
const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
|
|
4149
|
+
await sql4.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${idxName}`).execute(ddlDb);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4061
4152
|
const sg2 = await registerSubgraph(db, regData);
|
|
4062
4153
|
const addedCols = {};
|
|
4063
4154
|
for (const [t, colDiff] of Object.entries(diff.tables)) {
|
|
@@ -4068,7 +4159,9 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4068
4159
|
addedTables: diff.addedTables,
|
|
4069
4160
|
removedTables: [],
|
|
4070
4161
|
addedColumns: addedCols,
|
|
4071
|
-
breakingChanges: []
|
|
4162
|
+
breakingChanges: [],
|
|
4163
|
+
...Object.keys(flagFlips).length > 0 ? { indexChanges: flagFlips } : {},
|
|
4164
|
+
...Object.keys(addedUq).length > 0 ? { addedUniqueKeys: addedUq } : {}
|
|
4072
4165
|
};
|
|
4073
4166
|
return {
|
|
4074
4167
|
action: "updated",
|
|
@@ -4126,5 +4219,5 @@ export {
|
|
|
4126
4219
|
ByoBreakingChangeError
|
|
4127
4220
|
};
|
|
4128
4221
|
|
|
4129
|
-
//# debugId=
|
|
4222
|
+
//# debugId=E040C325764492FC64756E2164756E21
|
|
4130
4223
|
//# sourceMappingURL=index.js.map
|