@kernhq/module-quire 0.10.9 → 0.11.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/contract/models.d.ts +87 -0
- package/dist/contract/models.d.ts.map +1 -1
- package/dist/contract/models.js +73 -0
- package/dist/contract/models.js.map +1 -1
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +32 -0
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/router.d.ts +645 -0
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +137 -2
- package/dist/contract/router.js.map +1 -1
- package/dist/server/_impl.d.ts +877 -0
- package/dist/server/_impl.d.ts.map +1 -1
- package/dist/server/_impl.js +127 -1
- package/dist/server/_impl.js.map +1 -1
- package/dist/server/schema.d.ts +482 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +115 -1
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/index.d.ts +3 -0
- package/dist/server/services/index.d.ts.map +1 -1
- package/dist/server/services/index.js +3 -0
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/organisation.d.ts +117 -0
- package/dist/server/services/organisation.d.ts.map +1 -0
- package/dist/server/services/organisation.js +319 -0
- package/dist/server/services/organisation.js.map +1 -0
- package/dist/server/services/pages.d.ts +16 -1
- package/dist/server/services/pages.d.ts.map +1 -1
- package/dist/server/services/pages.js +62 -3
- package/dist/server/services/pages.js.map +1 -1
- package/migrations/0007_organisation.sql +114 -0
- package/migrations/meta/0007_snapshot.json +1588 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/src/client/components/ConfirmDialog.svelte +123 -0
- package/src/client/components/FavoriteStar.svelte +81 -0
- package/src/client/components/LabelChip.svelte +46 -0
- package/src/client/components/LabelManager.svelte +336 -0
- package/src/client/components/PageLabels.svelte +158 -0
- package/src/client/components/SidebarFavorites.svelte +262 -0
- package/src/client/components/SidebarRecents.svelte +98 -0
- package/src/client/components/SidebarSpaces.svelte +266 -1
- package/src/client/i18n.ts +387 -0
- package/src/client/index.ts +8 -0
- package/src/client/mock.ts +306 -0
- package/src/client/module.ts +18 -0
- package/src/client/pages/PageView.svelte +284 -9
- package/src/client/pages/TrashPage.svelte +378 -0
- package/src/client/query.ts +24 -0
- package/src/contract/models.ts +83 -0
- package/src/contract/permissions.ts +36 -0
- package/src/contract/router.ts +153 -1
package/dist/server/schema.d.ts
CHANGED
|
@@ -1769,6 +1769,487 @@ export declare const relations: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
1769
1769
|
};
|
|
1770
1770
|
dialect: "pg";
|
|
1771
1771
|
}>;
|
|
1772
|
+
/**
|
|
1773
|
+
* A word a space puts on pages, so they can be gathered by something other than where they sit.
|
|
1774
|
+
*
|
|
1775
|
+
* Scoped to a space rather than to the workspace: two teams both wanting "Draft" should not have to
|
|
1776
|
+
* agree on what it means, and a label list that spans every space is a list nobody can read.
|
|
1777
|
+
*
|
|
1778
|
+
* The unique index is on `lower(name)`, not on `name`. Case is not a distinction anybody means here
|
|
1779
|
+
* — "Draft" and "draft" next to each other in a picker read as a mistake in the data, and whichever
|
|
1780
|
+
* one a person clicks is a coin toss. Lowercasing in the constraint rather than in the column keeps
|
|
1781
|
+
* the capitalisation somebody chose while refusing the near-duplicate.
|
|
1782
|
+
*/
|
|
1783
|
+
export declare const labels: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1784
|
+
name: "labels";
|
|
1785
|
+
schema: "mod_quire";
|
|
1786
|
+
columns: {
|
|
1787
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
1788
|
+
name: "id";
|
|
1789
|
+
tableName: "labels";
|
|
1790
|
+
dataType: "string";
|
|
1791
|
+
columnType: "PgUUID";
|
|
1792
|
+
data: string;
|
|
1793
|
+
driverParam: string;
|
|
1794
|
+
notNull: true;
|
|
1795
|
+
hasDefault: true;
|
|
1796
|
+
isPrimaryKey: true;
|
|
1797
|
+
isAutoincrement: false;
|
|
1798
|
+
hasRuntimeDefault: false;
|
|
1799
|
+
enumValues: undefined;
|
|
1800
|
+
baseColumn: never;
|
|
1801
|
+
identity: undefined;
|
|
1802
|
+
generated: undefined;
|
|
1803
|
+
}, {}, {}>;
|
|
1804
|
+
workspaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1805
|
+
name: "workspace_id";
|
|
1806
|
+
tableName: "labels";
|
|
1807
|
+
dataType: "string";
|
|
1808
|
+
columnType: "PgUUID";
|
|
1809
|
+
data: string;
|
|
1810
|
+
driverParam: string;
|
|
1811
|
+
notNull: true;
|
|
1812
|
+
hasDefault: false;
|
|
1813
|
+
isPrimaryKey: false;
|
|
1814
|
+
isAutoincrement: false;
|
|
1815
|
+
hasRuntimeDefault: false;
|
|
1816
|
+
enumValues: undefined;
|
|
1817
|
+
baseColumn: never;
|
|
1818
|
+
identity: undefined;
|
|
1819
|
+
generated: undefined;
|
|
1820
|
+
}, {}, {}>;
|
|
1821
|
+
spaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1822
|
+
name: "space_id";
|
|
1823
|
+
tableName: "labels";
|
|
1824
|
+
dataType: "string";
|
|
1825
|
+
columnType: "PgUUID";
|
|
1826
|
+
data: string;
|
|
1827
|
+
driverParam: string;
|
|
1828
|
+
notNull: true;
|
|
1829
|
+
hasDefault: false;
|
|
1830
|
+
isPrimaryKey: false;
|
|
1831
|
+
isAutoincrement: false;
|
|
1832
|
+
hasRuntimeDefault: false;
|
|
1833
|
+
enumValues: undefined;
|
|
1834
|
+
baseColumn: never;
|
|
1835
|
+
identity: undefined;
|
|
1836
|
+
generated: undefined;
|
|
1837
|
+
}, {}, {}>;
|
|
1838
|
+
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
1839
|
+
name: "name";
|
|
1840
|
+
tableName: "labels";
|
|
1841
|
+
dataType: "string";
|
|
1842
|
+
columnType: "PgText";
|
|
1843
|
+
data: string;
|
|
1844
|
+
driverParam: string;
|
|
1845
|
+
notNull: true;
|
|
1846
|
+
hasDefault: false;
|
|
1847
|
+
isPrimaryKey: false;
|
|
1848
|
+
isAutoincrement: false;
|
|
1849
|
+
hasRuntimeDefault: false;
|
|
1850
|
+
enumValues: [string, ...string[]];
|
|
1851
|
+
baseColumn: never;
|
|
1852
|
+
identity: undefined;
|
|
1853
|
+
generated: undefined;
|
|
1854
|
+
}, {}, {}>;
|
|
1855
|
+
colour: import("drizzle-orm/pg-core").PgColumn<{
|
|
1856
|
+
name: "colour";
|
|
1857
|
+
tableName: "labels";
|
|
1858
|
+
dataType: "string";
|
|
1859
|
+
columnType: "PgText";
|
|
1860
|
+
data: string;
|
|
1861
|
+
driverParam: string;
|
|
1862
|
+
notNull: true;
|
|
1863
|
+
hasDefault: true;
|
|
1864
|
+
isPrimaryKey: false;
|
|
1865
|
+
isAutoincrement: false;
|
|
1866
|
+
hasRuntimeDefault: false;
|
|
1867
|
+
enumValues: [string, ...string[]];
|
|
1868
|
+
baseColumn: never;
|
|
1869
|
+
identity: undefined;
|
|
1870
|
+
generated: undefined;
|
|
1871
|
+
}, {}, {}>;
|
|
1872
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1873
|
+
name: string;
|
|
1874
|
+
tableName: "labels";
|
|
1875
|
+
dataType: "date";
|
|
1876
|
+
columnType: "PgTimestamp";
|
|
1877
|
+
data: Date;
|
|
1878
|
+
driverParam: string;
|
|
1879
|
+
notNull: true;
|
|
1880
|
+
hasDefault: true;
|
|
1881
|
+
isPrimaryKey: false;
|
|
1882
|
+
isAutoincrement: false;
|
|
1883
|
+
hasRuntimeDefault: false;
|
|
1884
|
+
enumValues: undefined;
|
|
1885
|
+
baseColumn: never;
|
|
1886
|
+
identity: undefined;
|
|
1887
|
+
generated: undefined;
|
|
1888
|
+
}, {}, {}>;
|
|
1889
|
+
};
|
|
1890
|
+
dialect: "pg";
|
|
1891
|
+
}>;
|
|
1892
|
+
/**
|
|
1893
|
+
* A label on a page.
|
|
1894
|
+
*
|
|
1895
|
+
* The primary key is composite and declared in the table's second argument. Two column-level
|
|
1896
|
+
* `.primaryKey()` calls are not a composite key — drizzle emits `PRIMARY KEY` on both columns and
|
|
1897
|
+
* Postgres refuses the table with "multiple primary keys for table are not allowed". Because the
|
|
1898
|
+
* kernel runs a module's migrations at boot, that is not a broken table but a host service that
|
|
1899
|
+
* never binds its port, taking every other module in the process down with it.
|
|
1900
|
+
*
|
|
1901
|
+
* `(page_id, label_id)` leads with the page because reading a page's labels is what happens on every
|
|
1902
|
+
* page load; the second index leads with the label, which is what a "everything tagged X" list asks.
|
|
1903
|
+
*/
|
|
1904
|
+
export declare const pageLabels: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1905
|
+
name: "page_labels";
|
|
1906
|
+
schema: "mod_quire";
|
|
1907
|
+
columns: {
|
|
1908
|
+
pageId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1909
|
+
name: "page_id";
|
|
1910
|
+
tableName: "page_labels";
|
|
1911
|
+
dataType: "string";
|
|
1912
|
+
columnType: "PgUUID";
|
|
1913
|
+
data: string;
|
|
1914
|
+
driverParam: string;
|
|
1915
|
+
notNull: true;
|
|
1916
|
+
hasDefault: false;
|
|
1917
|
+
isPrimaryKey: false;
|
|
1918
|
+
isAutoincrement: false;
|
|
1919
|
+
hasRuntimeDefault: false;
|
|
1920
|
+
enumValues: undefined;
|
|
1921
|
+
baseColumn: never;
|
|
1922
|
+
identity: undefined;
|
|
1923
|
+
generated: undefined;
|
|
1924
|
+
}, {}, {}>;
|
|
1925
|
+
labelId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1926
|
+
name: "label_id";
|
|
1927
|
+
tableName: "page_labels";
|
|
1928
|
+
dataType: "string";
|
|
1929
|
+
columnType: "PgUUID";
|
|
1930
|
+
data: string;
|
|
1931
|
+
driverParam: string;
|
|
1932
|
+
notNull: true;
|
|
1933
|
+
hasDefault: false;
|
|
1934
|
+
isPrimaryKey: false;
|
|
1935
|
+
isAutoincrement: false;
|
|
1936
|
+
hasRuntimeDefault: false;
|
|
1937
|
+
enumValues: undefined;
|
|
1938
|
+
baseColumn: never;
|
|
1939
|
+
identity: undefined;
|
|
1940
|
+
generated: undefined;
|
|
1941
|
+
}, {}, {}>;
|
|
1942
|
+
workspaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1943
|
+
name: "workspace_id";
|
|
1944
|
+
tableName: "page_labels";
|
|
1945
|
+
dataType: "string";
|
|
1946
|
+
columnType: "PgUUID";
|
|
1947
|
+
data: string;
|
|
1948
|
+
driverParam: string;
|
|
1949
|
+
notNull: true;
|
|
1950
|
+
hasDefault: false;
|
|
1951
|
+
isPrimaryKey: false;
|
|
1952
|
+
isAutoincrement: false;
|
|
1953
|
+
hasRuntimeDefault: false;
|
|
1954
|
+
enumValues: undefined;
|
|
1955
|
+
baseColumn: never;
|
|
1956
|
+
identity: undefined;
|
|
1957
|
+
generated: undefined;
|
|
1958
|
+
}, {}, {}>;
|
|
1959
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1960
|
+
name: string;
|
|
1961
|
+
tableName: "page_labels";
|
|
1962
|
+
dataType: "date";
|
|
1963
|
+
columnType: "PgTimestamp";
|
|
1964
|
+
data: Date;
|
|
1965
|
+
driverParam: string;
|
|
1966
|
+
notNull: true;
|
|
1967
|
+
hasDefault: true;
|
|
1968
|
+
isPrimaryKey: false;
|
|
1969
|
+
isAutoincrement: false;
|
|
1970
|
+
hasRuntimeDefault: false;
|
|
1971
|
+
enumValues: undefined;
|
|
1972
|
+
baseColumn: never;
|
|
1973
|
+
identity: undefined;
|
|
1974
|
+
generated: undefined;
|
|
1975
|
+
}, {}, {}>;
|
|
1976
|
+
};
|
|
1977
|
+
dialect: "pg";
|
|
1978
|
+
}>;
|
|
1979
|
+
/**
|
|
1980
|
+
* A page one person put in their sidebar, and where they put it.
|
|
1981
|
+
*
|
|
1982
|
+
* Per user, not per workspace: a favourite is somebody's own shortcut, so the key is the person and
|
|
1983
|
+
* the page rather than anything shared. `workspace_id` is carried anyway — it is what the row-level
|
|
1984
|
+
* policy reads, and without it a favourite would be the one row in this module not fenced by tenant.
|
|
1985
|
+
*
|
|
1986
|
+
* **`position` is `COLLATE "C"` in the migration and has to stay that way.** It is a fractional
|
|
1987
|
+
* index over a base-62 alphabet ordered by code point, so `ORDER BY position` is only the order the
|
|
1988
|
+
* algorithm intended under byte comparison; this database is `en_US.UTF-8`, where `'U' < 'c'` is
|
|
1989
|
+
* false and a list comes back shuffled. drizzle-kit does not carry collation in its snapshot, so if
|
|
1990
|
+
* this migration is ever regenerated, put it back — that is exactly how `properties.position` and
|
|
1991
|
+
* `views.position` lost theirs.
|
|
1992
|
+
*/
|
|
1993
|
+
export declare const favorites: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1994
|
+
name: "favorites";
|
|
1995
|
+
schema: "mod_quire";
|
|
1996
|
+
columns: {
|
|
1997
|
+
workspaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1998
|
+
name: "workspace_id";
|
|
1999
|
+
tableName: "favorites";
|
|
2000
|
+
dataType: "string";
|
|
2001
|
+
columnType: "PgUUID";
|
|
2002
|
+
data: string;
|
|
2003
|
+
driverParam: string;
|
|
2004
|
+
notNull: true;
|
|
2005
|
+
hasDefault: false;
|
|
2006
|
+
isPrimaryKey: false;
|
|
2007
|
+
isAutoincrement: false;
|
|
2008
|
+
hasRuntimeDefault: false;
|
|
2009
|
+
enumValues: undefined;
|
|
2010
|
+
baseColumn: never;
|
|
2011
|
+
identity: undefined;
|
|
2012
|
+
generated: undefined;
|
|
2013
|
+
}, {}, {}>;
|
|
2014
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2015
|
+
name: "user_id";
|
|
2016
|
+
tableName: "favorites";
|
|
2017
|
+
dataType: "string";
|
|
2018
|
+
columnType: "PgUUID";
|
|
2019
|
+
data: string;
|
|
2020
|
+
driverParam: string;
|
|
2021
|
+
notNull: true;
|
|
2022
|
+
hasDefault: false;
|
|
2023
|
+
isPrimaryKey: false;
|
|
2024
|
+
isAutoincrement: false;
|
|
2025
|
+
hasRuntimeDefault: false;
|
|
2026
|
+
enumValues: undefined;
|
|
2027
|
+
baseColumn: never;
|
|
2028
|
+
identity: undefined;
|
|
2029
|
+
generated: undefined;
|
|
2030
|
+
}, {}, {}>;
|
|
2031
|
+
pageId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2032
|
+
name: "page_id";
|
|
2033
|
+
tableName: "favorites";
|
|
2034
|
+
dataType: "string";
|
|
2035
|
+
columnType: "PgUUID";
|
|
2036
|
+
data: string;
|
|
2037
|
+
driverParam: string;
|
|
2038
|
+
notNull: true;
|
|
2039
|
+
hasDefault: false;
|
|
2040
|
+
isPrimaryKey: false;
|
|
2041
|
+
isAutoincrement: false;
|
|
2042
|
+
hasRuntimeDefault: false;
|
|
2043
|
+
enumValues: undefined;
|
|
2044
|
+
baseColumn: never;
|
|
2045
|
+
identity: undefined;
|
|
2046
|
+
generated: undefined;
|
|
2047
|
+
}, {}, {}>;
|
|
2048
|
+
position: import("drizzle-orm/pg-core").PgColumn<{
|
|
2049
|
+
name: "position";
|
|
2050
|
+
tableName: "favorites";
|
|
2051
|
+
dataType: "string";
|
|
2052
|
+
columnType: "PgText";
|
|
2053
|
+
data: string;
|
|
2054
|
+
driverParam: string;
|
|
2055
|
+
notNull: true;
|
|
2056
|
+
hasDefault: false;
|
|
2057
|
+
isPrimaryKey: false;
|
|
2058
|
+
isAutoincrement: false;
|
|
2059
|
+
hasRuntimeDefault: false;
|
|
2060
|
+
enumValues: [string, ...string[]];
|
|
2061
|
+
baseColumn: never;
|
|
2062
|
+
identity: undefined;
|
|
2063
|
+
generated: undefined;
|
|
2064
|
+
}, {}, {}>;
|
|
2065
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2066
|
+
name: string;
|
|
2067
|
+
tableName: "favorites";
|
|
2068
|
+
dataType: "date";
|
|
2069
|
+
columnType: "PgTimestamp";
|
|
2070
|
+
data: Date;
|
|
2071
|
+
driverParam: string;
|
|
2072
|
+
notNull: true;
|
|
2073
|
+
hasDefault: true;
|
|
2074
|
+
isPrimaryKey: false;
|
|
2075
|
+
isAutoincrement: false;
|
|
2076
|
+
hasRuntimeDefault: false;
|
|
2077
|
+
enumValues: undefined;
|
|
2078
|
+
baseColumn: never;
|
|
2079
|
+
identity: undefined;
|
|
2080
|
+
generated: undefined;
|
|
2081
|
+
}, {}, {}>;
|
|
2082
|
+
};
|
|
2083
|
+
dialect: "pg";
|
|
2084
|
+
}>;
|
|
2085
|
+
/**
|
|
2086
|
+
* The last time one person opened one page.
|
|
2087
|
+
*
|
|
2088
|
+
* One row per person per page, updated in place, rather than one row per visit: a log would grow
|
|
2089
|
+
* without bound to answer a question that only ever wants the most recent handful, and it would need
|
|
2090
|
+
* pruning nobody would remember to run. The write is an upsert on the key that bumps `viewed_at`, so
|
|
2091
|
+
* re-opening a page moves it up the list instead of adding to it.
|
|
2092
|
+
*/
|
|
2093
|
+
export declare const recentViews: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2094
|
+
name: "recent_views";
|
|
2095
|
+
schema: "mod_quire";
|
|
2096
|
+
columns: {
|
|
2097
|
+
workspaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2098
|
+
name: "workspace_id";
|
|
2099
|
+
tableName: "recent_views";
|
|
2100
|
+
dataType: "string";
|
|
2101
|
+
columnType: "PgUUID";
|
|
2102
|
+
data: string;
|
|
2103
|
+
driverParam: string;
|
|
2104
|
+
notNull: true;
|
|
2105
|
+
hasDefault: false;
|
|
2106
|
+
isPrimaryKey: false;
|
|
2107
|
+
isAutoincrement: false;
|
|
2108
|
+
hasRuntimeDefault: false;
|
|
2109
|
+
enumValues: undefined;
|
|
2110
|
+
baseColumn: never;
|
|
2111
|
+
identity: undefined;
|
|
2112
|
+
generated: undefined;
|
|
2113
|
+
}, {}, {}>;
|
|
2114
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2115
|
+
name: "user_id";
|
|
2116
|
+
tableName: "recent_views";
|
|
2117
|
+
dataType: "string";
|
|
2118
|
+
columnType: "PgUUID";
|
|
2119
|
+
data: string;
|
|
2120
|
+
driverParam: string;
|
|
2121
|
+
notNull: true;
|
|
2122
|
+
hasDefault: false;
|
|
2123
|
+
isPrimaryKey: false;
|
|
2124
|
+
isAutoincrement: false;
|
|
2125
|
+
hasRuntimeDefault: false;
|
|
2126
|
+
enumValues: undefined;
|
|
2127
|
+
baseColumn: never;
|
|
2128
|
+
identity: undefined;
|
|
2129
|
+
generated: undefined;
|
|
2130
|
+
}, {}, {}>;
|
|
2131
|
+
pageId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2132
|
+
name: "page_id";
|
|
2133
|
+
tableName: "recent_views";
|
|
2134
|
+
dataType: "string";
|
|
2135
|
+
columnType: "PgUUID";
|
|
2136
|
+
data: string;
|
|
2137
|
+
driverParam: string;
|
|
2138
|
+
notNull: true;
|
|
2139
|
+
hasDefault: false;
|
|
2140
|
+
isPrimaryKey: false;
|
|
2141
|
+
isAutoincrement: false;
|
|
2142
|
+
hasRuntimeDefault: false;
|
|
2143
|
+
enumValues: undefined;
|
|
2144
|
+
baseColumn: never;
|
|
2145
|
+
identity: undefined;
|
|
2146
|
+
generated: undefined;
|
|
2147
|
+
}, {}, {}>;
|
|
2148
|
+
viewedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2149
|
+
name: string;
|
|
2150
|
+
tableName: "recent_views";
|
|
2151
|
+
dataType: "date";
|
|
2152
|
+
columnType: "PgTimestamp";
|
|
2153
|
+
data: Date;
|
|
2154
|
+
driverParam: string;
|
|
2155
|
+
notNull: true;
|
|
2156
|
+
hasDefault: true;
|
|
2157
|
+
isPrimaryKey: false;
|
|
2158
|
+
isAutoincrement: false;
|
|
2159
|
+
hasRuntimeDefault: false;
|
|
2160
|
+
enumValues: undefined;
|
|
2161
|
+
baseColumn: never;
|
|
2162
|
+
identity: undefined;
|
|
2163
|
+
generated: undefined;
|
|
2164
|
+
}, {}, {}>;
|
|
2165
|
+
};
|
|
2166
|
+
dialect: "pg";
|
|
2167
|
+
}>;
|
|
2168
|
+
/**
|
|
2169
|
+
* Somebody who asked to hear about a page.
|
|
2170
|
+
*
|
|
2171
|
+
* Separate from `favorites` because they are different questions: a favourite is "I want this to
|
|
2172
|
+
* hand", a watch is "tell me when this changes". Collapsing them means either a sidebar full of
|
|
2173
|
+
* pages somebody only wanted notifications about, or a notification for every shortcut they made.
|
|
2174
|
+
*
|
|
2175
|
+
* The index leads with the page rather than the user, because the read that matters happens on
|
|
2176
|
+
* every edit — who has to be told about this — while a person's own list of watches is opened rarely.
|
|
2177
|
+
*/
|
|
2178
|
+
export declare const watchers: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2179
|
+
name: "watchers";
|
|
2180
|
+
schema: "mod_quire";
|
|
2181
|
+
columns: {
|
|
2182
|
+
workspaceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2183
|
+
name: "workspace_id";
|
|
2184
|
+
tableName: "watchers";
|
|
2185
|
+
dataType: "string";
|
|
2186
|
+
columnType: "PgUUID";
|
|
2187
|
+
data: string;
|
|
2188
|
+
driverParam: string;
|
|
2189
|
+
notNull: true;
|
|
2190
|
+
hasDefault: false;
|
|
2191
|
+
isPrimaryKey: false;
|
|
2192
|
+
isAutoincrement: false;
|
|
2193
|
+
hasRuntimeDefault: false;
|
|
2194
|
+
enumValues: undefined;
|
|
2195
|
+
baseColumn: never;
|
|
2196
|
+
identity: undefined;
|
|
2197
|
+
generated: undefined;
|
|
2198
|
+
}, {}, {}>;
|
|
2199
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2200
|
+
name: "user_id";
|
|
2201
|
+
tableName: "watchers";
|
|
2202
|
+
dataType: "string";
|
|
2203
|
+
columnType: "PgUUID";
|
|
2204
|
+
data: string;
|
|
2205
|
+
driverParam: string;
|
|
2206
|
+
notNull: true;
|
|
2207
|
+
hasDefault: false;
|
|
2208
|
+
isPrimaryKey: false;
|
|
2209
|
+
isAutoincrement: false;
|
|
2210
|
+
hasRuntimeDefault: false;
|
|
2211
|
+
enumValues: undefined;
|
|
2212
|
+
baseColumn: never;
|
|
2213
|
+
identity: undefined;
|
|
2214
|
+
generated: undefined;
|
|
2215
|
+
}, {}, {}>;
|
|
2216
|
+
pageId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2217
|
+
name: "page_id";
|
|
2218
|
+
tableName: "watchers";
|
|
2219
|
+
dataType: "string";
|
|
2220
|
+
columnType: "PgUUID";
|
|
2221
|
+
data: string;
|
|
2222
|
+
driverParam: string;
|
|
2223
|
+
notNull: true;
|
|
2224
|
+
hasDefault: false;
|
|
2225
|
+
isPrimaryKey: false;
|
|
2226
|
+
isAutoincrement: false;
|
|
2227
|
+
hasRuntimeDefault: false;
|
|
2228
|
+
enumValues: undefined;
|
|
2229
|
+
baseColumn: never;
|
|
2230
|
+
identity: undefined;
|
|
2231
|
+
generated: undefined;
|
|
2232
|
+
}, {}, {}>;
|
|
2233
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2234
|
+
name: string;
|
|
2235
|
+
tableName: "watchers";
|
|
2236
|
+
dataType: "date";
|
|
2237
|
+
columnType: "PgTimestamp";
|
|
2238
|
+
data: Date;
|
|
2239
|
+
driverParam: string;
|
|
2240
|
+
notNull: true;
|
|
2241
|
+
hasDefault: true;
|
|
2242
|
+
isPrimaryKey: false;
|
|
2243
|
+
isAutoincrement: false;
|
|
2244
|
+
hasRuntimeDefault: false;
|
|
2245
|
+
enumValues: undefined;
|
|
2246
|
+
baseColumn: never;
|
|
2247
|
+
identity: undefined;
|
|
2248
|
+
generated: undefined;
|
|
2249
|
+
}, {}, {}>;
|
|
2250
|
+
};
|
|
2251
|
+
dialect: "pg";
|
|
2252
|
+
}>;
|
|
1772
2253
|
/** Every tenant table, so the RLS migration can be checked against one list rather than memory. */
|
|
1773
|
-
export declare const TENANT_TABLES: readonly ["spaces", "pages", "page_versions", "comments", "databases", "properties", "views", "relations"];
|
|
2254
|
+
export declare const TENANT_TABLES: readonly ["spaces", "pages", "page_versions", "comments", "databases", "properties", "views", "relations", "labels", "page_labels", "favorites", "recent_views", "watchers"];
|
|
1774
2255
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/server/schema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/server/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/server/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
|
-
import { boolean, customType, index, integer, jsonb, pgSchema, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { boolean, customType, index, integer, jsonb, pgSchema, primaryKey, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
3
3
|
/**
|
|
4
4
|
* This module's tables, in its own Postgres schema.
|
|
5
5
|
*
|
|
@@ -249,6 +249,115 @@ export const relations = schema.table('relations', {
|
|
|
249
249
|
index('relations_ws_from_idx').on(t.workspaceId, t.fromPageId),
|
|
250
250
|
index('relations_ws_to_idx').on(t.workspaceId, t.toPageId),
|
|
251
251
|
]);
|
|
252
|
+
/**
|
|
253
|
+
* A word a space puts on pages, so they can be gathered by something other than where they sit.
|
|
254
|
+
*
|
|
255
|
+
* Scoped to a space rather than to the workspace: two teams both wanting "Draft" should not have to
|
|
256
|
+
* agree on what it means, and a label list that spans every space is a list nobody can read.
|
|
257
|
+
*
|
|
258
|
+
* The unique index is on `lower(name)`, not on `name`. Case is not a distinction anybody means here
|
|
259
|
+
* — "Draft" and "draft" next to each other in a picker read as a mistake in the data, and whichever
|
|
260
|
+
* one a person clicks is a coin toss. Lowercasing in the constraint rather than in the column keeps
|
|
261
|
+
* the capitalisation somebody chose while refusing the near-duplicate.
|
|
262
|
+
*/
|
|
263
|
+
export const labels = schema.table('labels', {
|
|
264
|
+
id: id(),
|
|
265
|
+
workspaceId: ws(),
|
|
266
|
+
spaceId: uuid('space_id').notNull(),
|
|
267
|
+
name: text('name').notNull(),
|
|
268
|
+
/**
|
|
269
|
+
* One of the closed set in `LabelColour`, stored as text. The contract is what closes it: the
|
|
270
|
+
* palette in `client/database/colours.ts` is the only place a name is turned into a colour pair
|
|
271
|
+
* that has been measured for contrast, and an unknown name falls back to grey rather than
|
|
272
|
+
* rendering a chip with no background.
|
|
273
|
+
*/
|
|
274
|
+
colour: text('colour').notNull().default('grey'),
|
|
275
|
+
createdAt: ts('created_at').notNull().defaultNow(),
|
|
276
|
+
}, (t) => [
|
|
277
|
+
uniqueIndex('labels_ws_space_name_uq').on(t.workspaceId, t.spaceId, sql `lower(${t.name})`),
|
|
278
|
+
index('labels_ws_space_idx').on(t.workspaceId, t.spaceId, t.name),
|
|
279
|
+
]);
|
|
280
|
+
/**
|
|
281
|
+
* A label on a page.
|
|
282
|
+
*
|
|
283
|
+
* The primary key is composite and declared in the table's second argument. Two column-level
|
|
284
|
+
* `.primaryKey()` calls are not a composite key — drizzle emits `PRIMARY KEY` on both columns and
|
|
285
|
+
* Postgres refuses the table with "multiple primary keys for table are not allowed". Because the
|
|
286
|
+
* kernel runs a module's migrations at boot, that is not a broken table but a host service that
|
|
287
|
+
* never binds its port, taking every other module in the process down with it.
|
|
288
|
+
*
|
|
289
|
+
* `(page_id, label_id)` leads with the page because reading a page's labels is what happens on every
|
|
290
|
+
* page load; the second index leads with the label, which is what a "everything tagged X" list asks.
|
|
291
|
+
*/
|
|
292
|
+
export const pageLabels = schema.table('page_labels', {
|
|
293
|
+
pageId: uuid('page_id').notNull(),
|
|
294
|
+
labelId: uuid('label_id').notNull(),
|
|
295
|
+
workspaceId: ws(),
|
|
296
|
+
createdAt: ts('created_at').notNull().defaultNow(),
|
|
297
|
+
}, (t) => [
|
|
298
|
+
primaryKey({ columns: [t.pageId, t.labelId] }),
|
|
299
|
+
index('page_labels_ws_label_idx').on(t.workspaceId, t.labelId, t.pageId),
|
|
300
|
+
]);
|
|
301
|
+
/**
|
|
302
|
+
* A page one person put in their sidebar, and where they put it.
|
|
303
|
+
*
|
|
304
|
+
* Per user, not per workspace: a favourite is somebody's own shortcut, so the key is the person and
|
|
305
|
+
* the page rather than anything shared. `workspace_id` is carried anyway — it is what the row-level
|
|
306
|
+
* policy reads, and without it a favourite would be the one row in this module not fenced by tenant.
|
|
307
|
+
*
|
|
308
|
+
* **`position` is `COLLATE "C"` in the migration and has to stay that way.** It is a fractional
|
|
309
|
+
* index over a base-62 alphabet ordered by code point, so `ORDER BY position` is only the order the
|
|
310
|
+
* algorithm intended under byte comparison; this database is `en_US.UTF-8`, where `'U' < 'c'` is
|
|
311
|
+
* false and a list comes back shuffled. drizzle-kit does not carry collation in its snapshot, so if
|
|
312
|
+
* this migration is ever regenerated, put it back — that is exactly how `properties.position` and
|
|
313
|
+
* `views.position` lost theirs.
|
|
314
|
+
*/
|
|
315
|
+
export const favorites = schema.table('favorites', {
|
|
316
|
+
workspaceId: ws(),
|
|
317
|
+
userId: uuid('user_id').notNull(),
|
|
318
|
+
pageId: uuid('page_id').notNull(),
|
|
319
|
+
position: text('position').notNull(),
|
|
320
|
+
createdAt: ts('created_at').notNull().defaultNow(),
|
|
321
|
+
}, (t) => [
|
|
322
|
+
primaryKey({ columns: [t.userId, t.pageId] }),
|
|
323
|
+
index('favorites_ws_user_idx').on(t.workspaceId, t.userId, t.position),
|
|
324
|
+
]);
|
|
325
|
+
/**
|
|
326
|
+
* The last time one person opened one page.
|
|
327
|
+
*
|
|
328
|
+
* One row per person per page, updated in place, rather than one row per visit: a log would grow
|
|
329
|
+
* without bound to answer a question that only ever wants the most recent handful, and it would need
|
|
330
|
+
* pruning nobody would remember to run. The write is an upsert on the key that bumps `viewed_at`, so
|
|
331
|
+
* re-opening a page moves it up the list instead of adding to it.
|
|
332
|
+
*/
|
|
333
|
+
export const recentViews = schema.table('recent_views', {
|
|
334
|
+
workspaceId: ws(),
|
|
335
|
+
userId: uuid('user_id').notNull(),
|
|
336
|
+
pageId: uuid('page_id').notNull(),
|
|
337
|
+
viewedAt: ts('viewed_at').notNull().defaultNow(),
|
|
338
|
+
}, (t) => [
|
|
339
|
+
primaryKey({ columns: [t.userId, t.pageId] }),
|
|
340
|
+
index('recent_views_ws_user_idx').on(t.workspaceId, t.userId, t.viewedAt.desc()),
|
|
341
|
+
]);
|
|
342
|
+
/**
|
|
343
|
+
* Somebody who asked to hear about a page.
|
|
344
|
+
*
|
|
345
|
+
* Separate from `favorites` because they are different questions: a favourite is "I want this to
|
|
346
|
+
* hand", a watch is "tell me when this changes". Collapsing them means either a sidebar full of
|
|
347
|
+
* pages somebody only wanted notifications about, or a notification for every shortcut they made.
|
|
348
|
+
*
|
|
349
|
+
* The index leads with the page rather than the user, because the read that matters happens on
|
|
350
|
+
* every edit — who has to be told about this — while a person's own list of watches is opened rarely.
|
|
351
|
+
*/
|
|
352
|
+
export const watchers = schema.table('watchers', {
|
|
353
|
+
workspaceId: ws(),
|
|
354
|
+
userId: uuid('user_id').notNull(),
|
|
355
|
+
pageId: uuid('page_id').notNull(),
|
|
356
|
+
createdAt: ts('created_at').notNull().defaultNow(),
|
|
357
|
+
}, (t) => [
|
|
358
|
+
primaryKey({ columns: [t.userId, t.pageId] }),
|
|
359
|
+
index('watchers_ws_page_idx').on(t.workspaceId, t.pageId, t.userId),
|
|
360
|
+
]);
|
|
252
361
|
/** Every tenant table, so the RLS migration can be checked against one list rather than memory. */
|
|
253
362
|
export const TENANT_TABLES = [
|
|
254
363
|
'spaces',
|
|
@@ -259,5 +368,10 @@ export const TENANT_TABLES = [
|
|
|
259
368
|
'properties',
|
|
260
369
|
'views',
|
|
261
370
|
'relations',
|
|
371
|
+
'labels',
|
|
372
|
+
'page_labels',
|
|
373
|
+
'favorites',
|
|
374
|
+
'recent_views',
|
|
375
|
+
'watchers',
|
|
262
376
|
];
|
|
263
377
|
//# sourceMappingURL=schema.js.map
|