@standardbeagle/edit-db 0.4.407 → 0.4.414

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.
@@ -0,0 +1,19 @@
1
+ import { Schema } from '../types/schema';
2
+ export interface CompositeTableRefValue {
3
+ /** Route-encoded composite key (`v1::v2`) suitable as a Select value. */
4
+ route: string;
5
+ /** Per-column raw values keyed by destination column name. */
6
+ values: Record<string, unknown>;
7
+ label: string;
8
+ }
9
+ export interface CompositeTableRef {
10
+ loading: boolean;
11
+ error: unknown;
12
+ data: CompositeTableRefValue[];
13
+ }
14
+ /**
15
+ * Fetch parent rows for a composite-FK Select widget. Returns one row per parent
16
+ * with its label plus every destination column value so the caller can write
17
+ * each composite-FK source column on the child form when a selection is made.
18
+ */
19
+ export declare function useCompositeTableRef(schema: Schema, destTableName: string, destColumnNames: string[]): CompositeTableRef;
@@ -1,4 +1,4 @@
1
- import { Table } from '../types/schema';
1
+ import { Table, Join } from '../types/schema';
2
2
  import { ColumnDef, ColumnFiltersState, SortingState } from '@tanstack/react-table';
3
3
  import { ColumnPanel } from '../data-panel';
4
4
  export { getFilterOperators } from '../lib/query-builder';
@@ -7,6 +7,7 @@ interface RowData {
7
7
  id?: number | string;
8
8
  [key: string]: unknown;
9
9
  }
10
+ export declare function getMultiJoinRows(row: RowData, join: Join): RowData[];
10
11
  /**
11
12
  * Return type for the useDataTable hook.
12
13
  * @interface UseDataTableResult
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { Join, Table } from '../types/schema';
2
+ export interface FkEqFilterResult {
3
+ filterText: string;
4
+ variables: Record<string, unknown>;
5
+ params: string[];
6
+ }
7
+ export declare function isFkMember(join: Join, columnName: string): boolean;
8
+ export declare function isComposite(join: Join): boolean;
9
+ export declare function findJoinBySource(joins: Join[], columnName: string): Join | undefined;
10
+ export declare function findJoinByDestinationTable(joins: Join[], destinationTable: string): Join | undefined;
11
+ /**
12
+ * Build a `{_eq}`-per-column composite filter against the destination side of a join,
13
+ * using values pulled from a source-side row. Single-column joins emit a single
14
+ * `{col: {_eq: $var}}` clause; composite joins are wrapped in `{and: [...]}`.
15
+ */
16
+ export declare function buildFkEqFilter(sourceRow: Record<string, unknown> | null | undefined, join: Join, destinationTable: Pick<Table, 'columns'> | undefined, varPrefix?: string): FkEqFilterResult | null;
17
+ /**
18
+ * Extract source-side column values from a row into a plain `{col: value}` object.
19
+ * Returns null if any source column is missing from the row, so callers can short-circuit.
20
+ */
21
+ export declare function fkSourceValues(sourceRow: Record<string, unknown> | null | undefined, join: Join): Record<string, unknown> | null;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { Column, Join, ManyToManyJoin, Table } from '../types/schema';
2
+ /** A tab in the detail panel: an ordinary child collection or a m2m bridge. */
3
+ export type DetailTab = {
4
+ kind: 'child';
5
+ key: string;
6
+ join: Join;
7
+ } | {
8
+ kind: 'm2m';
9
+ key: string;
10
+ m2m: ManyToManyJoin;
11
+ };
12
+ /** GraphQL names of every junction table reachable from `table` via a m2m join. */
13
+ export declare function junctionTableNames(table: Table): Set<string>;
14
+ /**
15
+ * The detail tabs for a parent table: its plain one-to-many children (minus any
16
+ * junction tables, which are folded into their m2m tab) followed by one tab per
17
+ * many-to-many bridge.
18
+ */
19
+ export declare function detailTabs(table: Table): DetailTab[];
20
+ /** Junction columns that are neither a primary key nor one of the two bridge FKs. */
21
+ export declare function payloadColumns(junction: Table, m2m: ManyToManyJoin): Column[];
22
+ /**
23
+ * Build the one-shot query that fetches the junction rows for a parent, each
24
+ * carrying its payload plus the nested target row (key + label). The grid
25
+ * flattens the target columns for display and keeps the junction PK so a link
26
+ * can be detached by deleting its junction row.
27
+ */
28
+ export declare function m2mRowsQuery(junction: Table, target: Table, m2m: ManyToManyJoin, parentId: string): {
29
+ query: string;
30
+ variables: Record<string, unknown>;
31
+ };
32
+ /**
33
+ * Read the target row's id and display label from a junction row's nested target
34
+ * selection (emitted by {@link m2mRowsQuery}). Returns null when the link has no
35
+ * resolved target. The label falls back to the id; the id is the target's first
36
+ * primary-key column, coerced to a string for routing.
37
+ */
38
+ export declare function targetDisplay(junctionRow: Record<string, unknown>, m2m: ManyToManyJoin, target: Table): {
39
+ id: string;
40
+ label: string;
41
+ } | null;
42
+ /**
43
+ * The insert detail for a new link: the parent key on the junction source FK and
44
+ * the chosen target key on the junction target FK. Any payload columns are left
45
+ * unset so the database applies its defaults; the user edits them afterwards.
46
+ */
47
+ export declare function attachJunctionDetail(m2m: ManyToManyJoin, parentId: string, targetId: string): Record<string, unknown>;
@@ -0,0 +1 @@
1
+ export {};
@@ -88,15 +88,44 @@ export interface Column {
88
88
  * @interface Join
89
89
  */
90
90
  export interface Join {
91
- /** Name of the join relationship */
91
+ /** Descriptive relationship name retained for compatibility; do not use it as the query field. */
92
92
  name: string;
93
+ /** GraphQL selection field for this relationship on the source table. May differ from destinationTable for aliases such as self-FK child collections. */
94
+ fieldName?: string;
93
95
  /** Column names in the source table */
94
96
  sourceColumnNames: string[];
95
- /** Target table name */
97
+ /** Target table/type name. Use for schema lookup and navigation, not as the relationship selection field when fieldName is present. */
96
98
  destinationTable: string;
97
99
  /** Column names in the target table */
98
100
  destinationColumnNames: string[];
99
101
  }
102
+ /**
103
+ * Many-to-many bridge between this entity and a target entity through a
104
+ * junction table. Projected by the server's `_dbSchema` resolver. The UI uses
105
+ * the junction's own multi-join for the rows query and these fields to skip
106
+ * the junction for navigation, drilling straight to the target entity.
107
+ * @interface ManyToManyJoin
108
+ */
109
+ export interface ManyToManyJoin {
110
+ /** Junction table GraphQL name (also the relationship name). */
111
+ name: string;
112
+ /** Target entity GraphQL type/table name. */
113
+ targetTable: string;
114
+ /** Junction table GraphQL type/table name. */
115
+ junctionTable: string;
116
+ /** Selection field on the junction type that resolves the target row. */
117
+ junctionTargetField: string;
118
+ /** Key column(s) on this (source) entity. */
119
+ sourceColumnNames: string[];
120
+ /** Junction column(s) referencing this (source) entity. */
121
+ junctionSourceColumnNames: string[];
122
+ /** Junction column(s) referencing the target entity. */
123
+ junctionTargetColumnNames: string[];
124
+ /** Key column(s) on the target entity. */
125
+ targetColumnNames: string[];
126
+ /** True when the junction carries extra (non-key) payload columns to reveal. */
127
+ hasPayload: boolean;
128
+ }
100
129
  /**
101
130
  * Table definition from database schema introspection.
102
131
  * @interface Table
@@ -124,6 +153,8 @@ export interface Table {
124
153
  multiJoins: Join[];
125
154
  /** Many-to-one relationship joins */
126
155
  singleJoins: Join[];
156
+ /** Many-to-many bridges through junction tables (optional; absent on older servers). */
157
+ manyToManyJoins?: ManyToManyJoin[];
127
158
  }
128
159
  /**
129
160
  * Schema context value containing database metadata and loading state.
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@standardbeagle/edit-db",
3
3
  "private": false,
4
- "version": "0.4.407",
4
+ "version": "0.4.414",
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "@radix-ui/react-slot": "^1.2.4",
8
8
  "@tanstack/react-form": "^1.32.0",
9
- "@tanstack/react-query": "^5.100.10",
9
+ "@tanstack/react-query": "^5.100.14",
10
10
  "@tanstack/react-table": "^8.21.3",
11
11
  "class-variance-authority": "^0.7.1",
12
12
  "clsx": "^2.1.1",
@@ -19,16 +19,13 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@babel/core": "^7.29.0",
22
- "@storybook/addon-actions": "^9.0.8",
23
- "@storybook/addon-essentials": "^8.6.14",
24
- "@storybook/addon-interactions": "^8.6.14",
25
- "@storybook/addon-links": "^10.4.0",
26
- "@storybook/react": "^10.4.0",
27
- "@storybook/react-vite": "^10.4.0",
22
+ "@storybook/addon-links": "^10.4.1",
23
+ "@storybook/react": "^10.4.1",
24
+ "@storybook/react-vite": "^10.4.1",
28
25
  "@tailwindcss/vite": "^4.3.0",
29
26
  "@testing-library/jest-dom": "^6.9.1",
30
27
  "@testing-library/react": "^16.3.2",
31
- "@types/react": "^18.3.28",
28
+ "@types/react": "^18.3.29",
32
29
  "@types/react-dom": "^18.3.7",
33
30
  "@types/redux-actions": "^2.6.5",
34
31
  "@typescript-eslint/eslint-plugin": "^7.18.0",
@@ -41,7 +38,7 @@
41
38
  "eslint-plugin-react-hooks": "^4.6.2",
42
39
  "eslint-plugin-react-refresh": "^0.4.26",
43
40
  "jsdom": "^25.0.1",
44
- "storybook": "^10.4.0",
41
+ "storybook": "^10.4.1",
45
42
  "tailwindcss": "^4.3.0",
46
43
  "typescript": "^5.9.3",
47
44
  "vite": "^7.3.3",