@instantdb/components 0.0.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/.env +2 -0
- package/.turbo/turbo-build.log +18 -0
- package/README.md +78 -0
- package/app/App.css +38 -0
- package/app/App.tsx +61 -0
- package/app/index.css +18 -0
- package/app/main.tsx +10 -0
- package/dist/components/StyleMe.d.ts +15 -0
- package/dist/components/StyleMe.d.ts.map +1 -0
- package/dist/components/error-boundary.d.ts +17 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/explorer/edit-namespace-dialog.d.ts +14 -0
- package/dist/components/explorer/edit-namespace-dialog.d.ts.map +1 -0
- package/dist/components/explorer/edit-row-dialog.d.ts +10 -0
- package/dist/components/explorer/edit-row-dialog.d.ts.map +1 -0
- package/dist/components/explorer/expandable-deleted-attr.d.ts +15 -0
- package/dist/components/explorer/expandable-deleted-attr.d.ts.map +1 -0
- package/dist/components/explorer/explorer-layout.d.ts +8 -0
- package/dist/components/explorer/explorer-layout.d.ts.map +1 -0
- package/dist/components/explorer/index.d.ts +44 -0
- package/dist/components/explorer/index.d.ts.map +1 -0
- package/dist/components/explorer/inner-explorer.d.ts +16 -0
- package/dist/components/explorer/inner-explorer.d.ts.map +1 -0
- package/dist/components/explorer/new-namespace-dialog.d.ts +10 -0
- package/dist/components/explorer/new-namespace-dialog.d.ts.map +1 -0
- package/dist/components/explorer/query-inspector.d.ts +11 -0
- package/dist/components/explorer/query-inspector.d.ts.map +1 -0
- package/dist/components/explorer/recently-deleted.d.ts +36 -0
- package/dist/components/explorer/recently-deleted.d.ts.map +1 -0
- package/dist/components/explorer/search-input.d.ts +9 -0
- package/dist/components/explorer/search-input.d.ts.map +1 -0
- package/dist/components/explorer/table-components.d.ts +16 -0
- package/dist/components/explorer/table-components.d.ts.map +1 -0
- package/dist/components/explorer/view-settings.d.ts +10 -0
- package/dist/components/explorer/view-settings.d.ts.map +1 -0
- package/dist/components/rosePineDawnTheme.d.ts +13 -0
- package/dist/components/rosePineDawnTheme.d.ts.map +1 -0
- package/dist/components/select.d.ts +16 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/toast.d.ts +4 -0
- package/dist/components/toast.d.ts.map +1 -0
- package/dist/components/ui.d.ts +336 -0
- package/dist/components/ui.d.ts.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/hooks/explorer.d.ts +29 -0
- package/dist/hooks/explorer.d.ts.map +1 -0
- package/dist/hooks/useAttrNotes.d.ts +10 -0
- package/dist/hooks/useAttrNotes.d.ts.map +1 -0
- package/dist/hooks/useClickOutside.d.ts +3 -0
- package/dist/hooks/useClickOutside.d.ts.map +1 -0
- package/dist/hooks/useColumnVisibility.d.ts +12 -0
- package/dist/hooks/useColumnVisibility.d.ts.map +1 -0
- package/dist/hooks/useEditBlobConstraints.d.ts +32 -0
- package/dist/hooks/useEditBlobConstraints.d.ts.map +1 -0
- package/dist/hooks/useExplorerHistory.d.ts +1 -0
- package/dist/hooks/useExplorerHistory.d.ts.map +1 -0
- package/dist/hooks/useIsOverflow.d.ts +6 -0
- package/dist/hooks/useIsOverflow.d.ts.map +1 -0
- package/dist/hooks/useLocalStorage.d.ts +2 -0
- package/dist/hooks/useLocalStorage.d.ts.map +1 -0
- package/dist/hooks/useMonacoJSONSchema.d.ts +3 -0
- package/dist/hooks/useMonacoJSONSchema.d.ts.map +1 -0
- package/dist/hooks/useStableDB.d.ts +7 -0
- package/dist/hooks/useStableDB.d.ts.map +1 -0
- package/dist/index.cjs +15 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9270 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/format.d.ts +2 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/indexingJobs.d.ts +24 -0
- package/dist/utils/indexingJobs.d.ts.map +1 -0
- package/dist/utils/parsePermsJSON.d.ts +11 -0
- package/dist/utils/parsePermsJSON.d.ts.map +1 -0
- package/dist/utils/renames.d.ts +3 -0
- package/dist/utils/renames.d.ts.map +1 -0
- package/dist/utils/tableWidthSize.d.ts +9 -0
- package/dist/utils/tableWidthSize.d.ts.map +1 -0
- package/index.html +13 -0
- package/package.json +109 -0
- package/src/components/StyleMe.tsx +97 -0
- package/src/components/error-boundary.tsx +76 -0
- package/src/components/explorer/edit-namespace-dialog.tsx +1886 -0
- package/src/components/explorer/edit-row-dialog.tsx +1151 -0
- package/src/components/explorer/expandable-deleted-attr.tsx +170 -0
- package/src/components/explorer/explorer-layout.tsx +156 -0
- package/src/components/explorer/index.tsx +217 -0
- package/src/components/explorer/inner-explorer.tsx +1341 -0
- package/src/components/explorer/new-namespace-dialog.tsx +54 -0
- package/src/components/explorer/query-inspector.tsx +394 -0
- package/src/components/explorer/recently-deleted.tsx +344 -0
- package/src/components/explorer/search-input.tsx +358 -0
- package/src/components/explorer/table-components.tsx +341 -0
- package/src/components/explorer/view-settings.tsx +75 -0
- package/src/components/rosePineDawnTheme.ts +45 -0
- package/src/components/select.tsx +198 -0
- package/src/components/toast.tsx +18 -0
- package/src/components/ui.tsx +1561 -0
- package/src/config.ts +61 -0
- package/src/hooks/explorer.tsx +125 -0
- package/src/hooks/useAttrNotes.ts +27 -0
- package/src/hooks/useClickOutside.ts +23 -0
- package/src/hooks/useColumnVisibility.ts +39 -0
- package/src/hooks/useEditBlobConstraints.ts +185 -0
- package/src/hooks/useExplorerHistory.ts +0 -0
- package/src/hooks/useIsOverflow.ts +24 -0
- package/src/hooks/useLocalStorage.ts +51 -0
- package/src/hooks/useMonacoJSONSchema.ts +41 -0
- package/src/hooks/useStableDB.ts +30 -0
- package/src/index.tsx +8 -0
- package/src/schema.ts +285 -0
- package/src/style.css +5 -0
- package/src/types.ts +359 -0
- package/src/utils/format.ts +13 -0
- package/src/utils/indexingJobs.ts +126 -0
- package/src/utils/parsePermsJSON.ts +35 -0
- package/src/utils/renames.ts +42 -0
- package/src/utils/tableWidthSize.ts +62 -0
- package/tailwind.config.cjs +42 -0
- package/tsconfig.json +22 -0
- package/vite-env.d.ts +1 -0
- package/vite.config.ts +49 -0
package/src/schema.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { DBAttr, SchemaAttr, SchemaNamespace } from '@lib/types';
|
|
2
|
+
import { InstantDBAttr } from '@instantdb/core';
|
|
3
|
+
import { InstantAPIPlatformSchema } from '@instantdb/platform';
|
|
4
|
+
|
|
5
|
+
// We show most attrs in the explorer except for some system attrs
|
|
6
|
+
function isVisibleAttr(attr: DBAttr) {
|
|
7
|
+
const [, namespace, label] = attr['forward-identity'];
|
|
8
|
+
return (
|
|
9
|
+
attr.catalog !== 'system' ||
|
|
10
|
+
namespace === '$users' ||
|
|
11
|
+
namespace === '$files'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function dbAttrsToExplorerSchema(
|
|
16
|
+
rawAttrs: Record<string, DBAttr>,
|
|
17
|
+
): SchemaNamespace[] {
|
|
18
|
+
const nsMap: Record<
|
|
19
|
+
string,
|
|
20
|
+
{ id: string; name: string; attrs: Record<string, SchemaAttr> }
|
|
21
|
+
> = {};
|
|
22
|
+
|
|
23
|
+
const oAttrs: Record<string, DBAttr> = {};
|
|
24
|
+
for (const [id, attrDesc] of Object.entries(rawAttrs)) {
|
|
25
|
+
if (!isVisibleAttr(attrDesc)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
oAttrs[id] = attrDesc;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const attrDesc of Object.values(oAttrs)) {
|
|
32
|
+
const [, namespace] = attrDesc['forward-identity'];
|
|
33
|
+
|
|
34
|
+
if (nsMap[namespace]) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
nsMap[namespace] = { id: namespace, name: namespace, attrs: {} };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const attrDesc of Object.values(oAttrs)) {
|
|
42
|
+
const linkConfig = {
|
|
43
|
+
forward: {
|
|
44
|
+
id: attrDesc['forward-identity'][0],
|
|
45
|
+
namespace: attrDesc['forward-identity'][1],
|
|
46
|
+
attr: attrDesc['forward-identity'][2],
|
|
47
|
+
nsMap: nsMap[attrDesc['forward-identity'][1]],
|
|
48
|
+
},
|
|
49
|
+
reverse: attrDesc['reverse-identity']
|
|
50
|
+
? {
|
|
51
|
+
id: attrDesc['reverse-identity'][0],
|
|
52
|
+
namespace: attrDesc['reverse-identity'][1],
|
|
53
|
+
attr: attrDesc['reverse-identity'][2],
|
|
54
|
+
nsMap: nsMap[attrDesc['reverse-identity'][1]],
|
|
55
|
+
}
|
|
56
|
+
: undefined,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (attrDesc['forward-identity']) {
|
|
60
|
+
const [fwdId, ns, attr, fwdIndexed] = attrDesc['forward-identity'];
|
|
61
|
+
const id = attrDesc.id + '-forward';
|
|
62
|
+
|
|
63
|
+
if (fwdIndexed !== false) {
|
|
64
|
+
nsMap[ns].attrs[id] = {
|
|
65
|
+
id: attrDesc.id,
|
|
66
|
+
isForward: true,
|
|
67
|
+
namespace: ns,
|
|
68
|
+
name: attr,
|
|
69
|
+
type: attrDesc['value-type'],
|
|
70
|
+
isIndex: attrDesc['index?'],
|
|
71
|
+
isUniq: attrDesc['unique?'],
|
|
72
|
+
isRequired: attrDesc['required?'],
|
|
73
|
+
isPrimary: attrDesc['primary?'],
|
|
74
|
+
cardinality: attrDesc.cardinality,
|
|
75
|
+
linkConfig,
|
|
76
|
+
inferredTypes: attrDesc['inferred-types'],
|
|
77
|
+
catalog: attrDesc.catalog,
|
|
78
|
+
checkedDataType: attrDesc['checked-data-type'],
|
|
79
|
+
sortable: attrDesc['index?'] && !!attrDesc['checked-data-type'],
|
|
80
|
+
onDelete: attrDesc['on-delete'],
|
|
81
|
+
onDeleteReverse: attrDesc['on-delete-reverse'],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!attrDesc['reverse-identity']) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const [revId, revNs, revAttr, revIndexed] = attrDesc['reverse-identity'];
|
|
91
|
+
|
|
92
|
+
// TODO: sometimes reverse-identity doesn't correspond to a real namespace
|
|
93
|
+
if (nsMap[revNs] && revIndexed !== false) {
|
|
94
|
+
const idr = attrDesc.id + '-reverse';
|
|
95
|
+
nsMap[revNs].attrs[idr] = {
|
|
96
|
+
id: attrDesc.id,
|
|
97
|
+
isForward: false,
|
|
98
|
+
namespace: revNs,
|
|
99
|
+
name: revAttr,
|
|
100
|
+
type: attrDesc['value-type'],
|
|
101
|
+
isIndex: attrDesc['index?'],
|
|
102
|
+
isUniq: attrDesc['unique?'],
|
|
103
|
+
isRequired: attrDesc['required?'],
|
|
104
|
+
cardinality: attrDesc.cardinality,
|
|
105
|
+
linkConfig,
|
|
106
|
+
sortable: attrDesc['index?'] && !!attrDesc['checked-data-type'],
|
|
107
|
+
onDelete: attrDesc['on-delete'],
|
|
108
|
+
onDeleteReverse: attrDesc['on-delete-reverse'],
|
|
109
|
+
catalog: attrDesc.catalog,
|
|
110
|
+
checkedDataType: attrDesc['checked-data-type'],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const namespaces = Object.values(nsMap)
|
|
116
|
+
.map((ns) => ({
|
|
117
|
+
...ns,
|
|
118
|
+
attrs: Object.values(ns.attrs).sort(nameComparator),
|
|
119
|
+
}))
|
|
120
|
+
.sort(nameComparator);
|
|
121
|
+
|
|
122
|
+
return namespaces;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function nameComparator(a: { name: string }, b: { name: string }) {
|
|
126
|
+
if (a.name === 'id') return -1;
|
|
127
|
+
if (b.name === 'id') return 1;
|
|
128
|
+
|
|
129
|
+
if (a.name < b.name) {
|
|
130
|
+
return -1;
|
|
131
|
+
}
|
|
132
|
+
if (a.name > b.name) {
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// attrsToSchema
|
|
139
|
+
// Converts attrs to the instant API schema format
|
|
140
|
+
// Make sure any changes here match changes to instant.model.schema in Clojure
|
|
141
|
+
|
|
142
|
+
// Helpers
|
|
143
|
+
|
|
144
|
+
const FILES_URL_AID = '96653230-13ff-ffff-2a35-48afffffffff';
|
|
145
|
+
|
|
146
|
+
function dbAttrToInstantDBAttr(attr: DBAttr): InstantDBAttr {
|
|
147
|
+
return {
|
|
148
|
+
...attr,
|
|
149
|
+
'required?': attr['required?'] ?? false,
|
|
150
|
+
'inferred-types': attr['inferred-types'] ?? null,
|
|
151
|
+
catalog: attr['catalog'] ?? 'user',
|
|
152
|
+
'forward-identity': [
|
|
153
|
+
attr['forward-identity'][0],
|
|
154
|
+
attr['forward-identity'][1],
|
|
155
|
+
attr['forward-identity'][2],
|
|
156
|
+
],
|
|
157
|
+
'reverse-identity': attr['reverse-identity']
|
|
158
|
+
? [
|
|
159
|
+
attr['reverse-identity'][0],
|
|
160
|
+
attr['reverse-identity'][1],
|
|
161
|
+
attr['reverse-identity'][2],
|
|
162
|
+
]
|
|
163
|
+
: undefined,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Transform $files.url attribute to mark it as required
|
|
168
|
+
function transformFilesUrlAttr(attr: DBAttr) {
|
|
169
|
+
// $files.url is a derived attribute that we always return from queries.
|
|
170
|
+
// It does not exist inside our database, so it's marked as optional.
|
|
171
|
+
// However, to our users, it's seen as a required attribute, since we always
|
|
172
|
+
// provide it.
|
|
173
|
+
if (attr.id === FILES_URL_AID) {
|
|
174
|
+
return { ...attr, required: true };
|
|
175
|
+
}
|
|
176
|
+
return attr;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Remove hidden system attributes
|
|
180
|
+
function removeHidden(attrs: DBAttr[]) {
|
|
181
|
+
return attrs.filter((attr) => {
|
|
182
|
+
const catalog = attr.catalog;
|
|
183
|
+
const fwdEtype = getFwdEtype(attr);
|
|
184
|
+
const fwdLabel = getFwdLabel(attr);
|
|
185
|
+
|
|
186
|
+
// Remove system attrs except $users and $files
|
|
187
|
+
if (catalog === 'system' && !['$users', '$files'].includes(fwdEtype)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Remove specific $files attributes
|
|
192
|
+
if (
|
|
193
|
+
fwdEtype === '$files' &&
|
|
194
|
+
[
|
|
195
|
+
'content-type',
|
|
196
|
+
'content-disposition',
|
|
197
|
+
'size',
|
|
198
|
+
'location-id',
|
|
199
|
+
'key-version',
|
|
200
|
+
].includes(fwdLabel)
|
|
201
|
+
) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return true;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function getFwdEtype(attr: DBAttr) {
|
|
210
|
+
return attr['forward-identity'][1];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getFwdLabel(attr: DBAttr) {
|
|
214
|
+
return attr['forward-identity'][2];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Make sure any changes here match changes to instant.model.schema in Clojure
|
|
218
|
+
// Converts attrs to the instant API schema format
|
|
219
|
+
export function attrsToSchema(attrs: DBAttr[]): InstantAPIPlatformSchema {
|
|
220
|
+
const filteredAttrs = removeHidden(attrs).map(transformFilesUrlAttr);
|
|
221
|
+
|
|
222
|
+
const grouped = filteredAttrs.reduce(
|
|
223
|
+
(acc: { ref: DBAttr[]; blob: DBAttr[] }, attr) => {
|
|
224
|
+
const valueType = attr['value-type'];
|
|
225
|
+
if (!acc[valueType]) {
|
|
226
|
+
acc[valueType] = [];
|
|
227
|
+
}
|
|
228
|
+
acc[valueType].push(attr);
|
|
229
|
+
return acc;
|
|
230
|
+
},
|
|
231
|
+
{ ref: [], blob: [] },
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const blobs = grouped.blob || [];
|
|
235
|
+
const refs = grouped.ref || [];
|
|
236
|
+
|
|
237
|
+
const refsIndexed: InstantAPIPlatformSchema['refs'] = refs.reduce(
|
|
238
|
+
(acc: InstantAPIPlatformSchema['refs'], attr) => {
|
|
239
|
+
const {
|
|
240
|
+
'forward-identity': forwardIdentity,
|
|
241
|
+
'reverse-identity': reverseIdentity,
|
|
242
|
+
} = attr;
|
|
243
|
+
const key = JSON.stringify([
|
|
244
|
+
forwardIdentity[1],
|
|
245
|
+
forwardIdentity[2],
|
|
246
|
+
reverseIdentity?.[1],
|
|
247
|
+
reverseIdentity?.[2],
|
|
248
|
+
]);
|
|
249
|
+
acc[key] = dbAttrToInstantDBAttr(attr);
|
|
250
|
+
return acc;
|
|
251
|
+
},
|
|
252
|
+
{},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const blobsGrouped: Record<string, DBAttr[]> = blobs.reduce(
|
|
256
|
+
(acc: Record<string, DBAttr[]>, blob) => {
|
|
257
|
+
const entityType = getFwdEtype(blob);
|
|
258
|
+
if (!acc[entityType]) {
|
|
259
|
+
acc[entityType] = [];
|
|
260
|
+
}
|
|
261
|
+
acc[entityType].push(blob);
|
|
262
|
+
return acc;
|
|
263
|
+
},
|
|
264
|
+
{},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const blobsIndexed: InstantAPIPlatformSchema['blobs'] = Object.entries(
|
|
268
|
+
blobsGrouped,
|
|
269
|
+
).reduce((acc: InstantAPIPlatformSchema['blobs'], [entityType, attrs]) => {
|
|
270
|
+
acc[entityType] = attrs.reduce(
|
|
271
|
+
(attrMap: Record<string, InstantDBAttr>, attr) => {
|
|
272
|
+
const attrName = attr['forward-identity'][2];
|
|
273
|
+
attrMap[attrName] = dbAttrToInstantDBAttr(attr);
|
|
274
|
+
return attrMap;
|
|
275
|
+
},
|
|
276
|
+
{},
|
|
277
|
+
);
|
|
278
|
+
return acc;
|
|
279
|
+
}, {});
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
refs: refsIndexed,
|
|
283
|
+
blobs: blobsIndexed,
|
|
284
|
+
};
|
|
285
|
+
}
|
package/src/style.css
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
export type RelationshipKinds =
|
|
2
|
+
| `many-many`
|
|
3
|
+
| 'one-one'
|
|
4
|
+
| 'one-many'
|
|
5
|
+
| 'many-one';
|
|
6
|
+
|
|
7
|
+
export const relationshipConstraints: Record<
|
|
8
|
+
RelationshipKinds,
|
|
9
|
+
{ cardinality: 'one' | 'many'; 'unique?': boolean }
|
|
10
|
+
> = {
|
|
11
|
+
/**
|
|
12
|
+
* users has_many tags
|
|
13
|
+
* tags has_many users
|
|
14
|
+
*/
|
|
15
|
+
'many-many': {
|
|
16
|
+
cardinality: 'many',
|
|
17
|
+
'unique?': false,
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* users has_one profile
|
|
21
|
+
* profiles has_one owner
|
|
22
|
+
*/
|
|
23
|
+
'one-one': {
|
|
24
|
+
cardinality: 'one',
|
|
25
|
+
'unique?': true,
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* users has_many posts
|
|
29
|
+
* posts has_one author
|
|
30
|
+
* [?users :users/posts ?posts]
|
|
31
|
+
* <---------------> unique!
|
|
32
|
+
*/
|
|
33
|
+
'many-one': {
|
|
34
|
+
cardinality: 'many',
|
|
35
|
+
'unique?': true,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* posts has_one owner
|
|
40
|
+
* users has_many owned_posts
|
|
41
|
+
* [?posts :posts/user ?users]
|
|
42
|
+
* <---------------> unique!
|
|
43
|
+
*/
|
|
44
|
+
'one-many': {
|
|
45
|
+
cardinality: 'one',
|
|
46
|
+
'unique?': false,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const relationshipConstraintsInverse: Record<string, RelationshipKinds> =
|
|
51
|
+
Object.fromEntries(
|
|
52
|
+
Object.entries(relationshipConstraints).map(([k, v]) => [
|
|
53
|
+
`${v.cardinality}-${v['unique?']}`,
|
|
54
|
+
k as RelationshipKinds,
|
|
55
|
+
]),
|
|
56
|
+
);
|
|
57
|
+
// COPIED FROM www/lib/types!!!!!!!!!!! LOOK OUT
|
|
58
|
+
|
|
59
|
+
import { InstantIssue } from '@instantdb/core';
|
|
60
|
+
|
|
61
|
+
export type InstantApp = {
|
|
62
|
+
id: string;
|
|
63
|
+
pro: boolean;
|
|
64
|
+
title: string;
|
|
65
|
+
created_at: string;
|
|
66
|
+
admin_token: string;
|
|
67
|
+
rules: object | null;
|
|
68
|
+
user_app_role: 'owner' | 'admin' | 'collaborator';
|
|
69
|
+
members: InstantMember[] | null;
|
|
70
|
+
invites: InstantAppInvite | null;
|
|
71
|
+
magic_code_email_template: {
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
email?: string;
|
|
75
|
+
body: string;
|
|
76
|
+
subject: string;
|
|
77
|
+
} | null;
|
|
78
|
+
org: { id: string; title: string } | null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type InstantMember = {
|
|
82
|
+
id: string;
|
|
83
|
+
email: string;
|
|
84
|
+
role: 'admin' | 'collaborator';
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
type InstantMemberInvite = {
|
|
88
|
+
id: string;
|
|
89
|
+
type: 'app' | 'org';
|
|
90
|
+
foreign_key: string;
|
|
91
|
+
title: string;
|
|
92
|
+
invitee_role: 'admin' | 'collaborator' | 'owner';
|
|
93
|
+
inviter_email: string;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export type InstantAppInvite = {
|
|
97
|
+
id: string;
|
|
98
|
+
email: string;
|
|
99
|
+
status: 'pending' | 'accepted' | 'revoked';
|
|
100
|
+
role: 'admin' | 'collaborator';
|
|
101
|
+
sent_at: string;
|
|
102
|
+
expired: boolean;
|
|
103
|
+
}[];
|
|
104
|
+
|
|
105
|
+
export type InstantIndexingJobInvalidTriple = {
|
|
106
|
+
entity_id: string;
|
|
107
|
+
value: any;
|
|
108
|
+
json_type:
|
|
109
|
+
| 'string'
|
|
110
|
+
| 'number'
|
|
111
|
+
| 'boolean'
|
|
112
|
+
| 'null'
|
|
113
|
+
| 'object'
|
|
114
|
+
| 'array'
|
|
115
|
+
| 'date';
|
|
116
|
+
};
|
|
117
|
+
export type InstantIndexingJob = {
|
|
118
|
+
id: string;
|
|
119
|
+
app_id: string;
|
|
120
|
+
attr_id: string;
|
|
121
|
+
job_type:
|
|
122
|
+
| 'remove-data-type'
|
|
123
|
+
| 'check-data-type'
|
|
124
|
+
| 'index'
|
|
125
|
+
| 'remove-index'
|
|
126
|
+
| 'unique'
|
|
127
|
+
| 'remove-unique'
|
|
128
|
+
| 'required'
|
|
129
|
+
| 'remove-required'
|
|
130
|
+
| string;
|
|
131
|
+
job_status: 'completed' | 'waiting' | 'processing' | 'errored' | string;
|
|
132
|
+
job_stage: string;
|
|
133
|
+
work_estimate: number | null | undefined;
|
|
134
|
+
work_completed: number | null | undefined;
|
|
135
|
+
error:
|
|
136
|
+
| 'invalid-triple-error'
|
|
137
|
+
| 'invalid-attr-state-error'
|
|
138
|
+
| 'triple-not-unique-error'
|
|
139
|
+
| 'triple-too-large-error'
|
|
140
|
+
| 'missing-required-error'
|
|
141
|
+
| 'unexpected-error'
|
|
142
|
+
| string
|
|
143
|
+
| null
|
|
144
|
+
| undefined;
|
|
145
|
+
error_data?: {
|
|
146
|
+
count: number;
|
|
147
|
+
'entity-ids': number[];
|
|
148
|
+
};
|
|
149
|
+
checked_data_type: CheckedDataType | null | undefined;
|
|
150
|
+
created_at: string;
|
|
151
|
+
updated_at: string;
|
|
152
|
+
done_at: string;
|
|
153
|
+
invalid_unique_value: any;
|
|
154
|
+
invalid_triples_sample: InstantIndexingJobInvalidTriple[] | null | undefined;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export type OrgSummary = {
|
|
158
|
+
id: string;
|
|
159
|
+
title: string;
|
|
160
|
+
created_at: string;
|
|
161
|
+
role: 'owner' | 'admin' | 'collaborator' | 'app-member';
|
|
162
|
+
paid: boolean;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type DashResponse = {
|
|
166
|
+
apps: InstantApp[];
|
|
167
|
+
invites?: InstantMemberInvite[];
|
|
168
|
+
user: {
|
|
169
|
+
email: string;
|
|
170
|
+
id: string;
|
|
171
|
+
};
|
|
172
|
+
orgs?: OrgSummary[];
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export type AppError = { body: { message: string } | undefined };
|
|
176
|
+
|
|
177
|
+
export type AuthorizedOriginService =
|
|
178
|
+
| 'generic'
|
|
179
|
+
| 'vercel'
|
|
180
|
+
| 'netlify'
|
|
181
|
+
| 'custom-scheme';
|
|
182
|
+
|
|
183
|
+
export type AuthorizedOrigin = {
|
|
184
|
+
id: string;
|
|
185
|
+
service: AuthorizedOriginService;
|
|
186
|
+
params: string[];
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export type OAuthServiceProvider = {
|
|
190
|
+
id: string;
|
|
191
|
+
provider_name: string;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export type OAuthClient = {
|
|
195
|
+
id: string;
|
|
196
|
+
client_name: string;
|
|
197
|
+
client_id?: string;
|
|
198
|
+
provider_id: string;
|
|
199
|
+
authorization_endpoint?: string;
|
|
200
|
+
token_endpoint?: string;
|
|
201
|
+
discovery_endpoint?: string;
|
|
202
|
+
meta?: any;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export type AppsAuthResponse = {
|
|
206
|
+
authorized_redirect_origins: AuthorizedOrigin[] | null | undefined;
|
|
207
|
+
oauth_service_providers: OAuthServiceProvider[] | null | undefined;
|
|
208
|
+
oauth_clients: OAuthClient[] | null | undefined;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export type SubscriptionName = 'Free' | 'Pro';
|
|
212
|
+
|
|
213
|
+
export type AppsSubscriptionResponse = {
|
|
214
|
+
'subscription-name': SubscriptionName;
|
|
215
|
+
'total-app-bytes': number;
|
|
216
|
+
'total-storage-bytes': number;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type DBIdent =
|
|
220
|
+
| [string, string, string]
|
|
221
|
+
| [string, string, string, boolean];
|
|
222
|
+
|
|
223
|
+
export type CheckedDataType = 'string' | 'number' | 'boolean' | 'date';
|
|
224
|
+
|
|
225
|
+
export interface DBAttr {
|
|
226
|
+
id: string;
|
|
227
|
+
'forward-identity': DBIdent;
|
|
228
|
+
'reverse-identity'?: DBIdent;
|
|
229
|
+
'index?': boolean;
|
|
230
|
+
'unique?': boolean;
|
|
231
|
+
'required?'?: boolean;
|
|
232
|
+
'primary?'?: boolean | undefined;
|
|
233
|
+
cardinality: 'one' | 'many';
|
|
234
|
+
'value-type': 'ref' | 'blob';
|
|
235
|
+
'inferred-types'?: Array<'string' | 'number' | 'boolean' | 'json'>;
|
|
236
|
+
catalog?: 'user' | 'system';
|
|
237
|
+
'checked-data-type'?: CheckedDataType;
|
|
238
|
+
'on-delete'?: 'cascade';
|
|
239
|
+
'on-delete-reverse'?: 'cascade';
|
|
240
|
+
metadata?: any;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface SchemaNamespace {
|
|
244
|
+
id: string;
|
|
245
|
+
name: string;
|
|
246
|
+
attrs: SchemaAttr[];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface SchemaNamespaceMap {
|
|
250
|
+
id: string;
|
|
251
|
+
name: string;
|
|
252
|
+
attrs: Record<string, SchemaAttr>;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface SchemaAttr {
|
|
256
|
+
id: string;
|
|
257
|
+
isForward: boolean;
|
|
258
|
+
namespace: string;
|
|
259
|
+
name: string;
|
|
260
|
+
type: 'ref' | 'blob';
|
|
261
|
+
isIndex: boolean;
|
|
262
|
+
isUniq: boolean;
|
|
263
|
+
isRequired?: boolean;
|
|
264
|
+
isPrimary?: boolean | undefined;
|
|
265
|
+
cardinality: 'one' | 'many';
|
|
266
|
+
linkConfig: {
|
|
267
|
+
forward: {
|
|
268
|
+
id: string;
|
|
269
|
+
namespace: string;
|
|
270
|
+
attr: string;
|
|
271
|
+
nsMap: SchemaNamespaceMap;
|
|
272
|
+
};
|
|
273
|
+
reverse:
|
|
274
|
+
| {
|
|
275
|
+
id: string;
|
|
276
|
+
namespace: string;
|
|
277
|
+
attr: string;
|
|
278
|
+
nsMap: SchemaNamespaceMap;
|
|
279
|
+
}
|
|
280
|
+
| undefined;
|
|
281
|
+
};
|
|
282
|
+
inferredTypes?: Array<'string' | 'number' | 'boolean' | 'json'>;
|
|
283
|
+
catalog?: 'user' | 'system';
|
|
284
|
+
checkedDataType?: CheckedDataType;
|
|
285
|
+
sortable: boolean;
|
|
286
|
+
onDelete?: 'cascade';
|
|
287
|
+
onDeleteReverse?: 'cascade';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export type OAuthAppClientSecret = {
|
|
291
|
+
id: string;
|
|
292
|
+
clientId: string;
|
|
293
|
+
firstFour: string;
|
|
294
|
+
createdAt: string;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export type OAuthAppClient = {
|
|
298
|
+
clientId: string;
|
|
299
|
+
oauthAppId: string;
|
|
300
|
+
clientName: string;
|
|
301
|
+
authorizedRedirectUrls: string[] | null;
|
|
302
|
+
clientSecrets: OAuthAppClientSecret[] | null;
|
|
303
|
+
createdAt: string;
|
|
304
|
+
updatedAt: string;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
export type OAuthApp = {
|
|
308
|
+
id: string;
|
|
309
|
+
appId: string;
|
|
310
|
+
appName: string;
|
|
311
|
+
grantedScopes: string[];
|
|
312
|
+
isPublic: boolean;
|
|
313
|
+
supportEmail: string | null;
|
|
314
|
+
appHomePage: string | null;
|
|
315
|
+
appPrivacyPolicyLink: string | null;
|
|
316
|
+
appTosLink: string | null;
|
|
317
|
+
appLogo: string | null;
|
|
318
|
+
clients: OAuthAppClient[] | null;
|
|
319
|
+
createdAt: string;
|
|
320
|
+
updatedAt: string;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export type OAuthAppsResponse = {
|
|
324
|
+
apps: OAuthApp[];
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// re-export InstantIssue from the core library
|
|
328
|
+
export { type InstantIssue };
|
|
329
|
+
|
|
330
|
+
export type HasDefault<T> = {
|
|
331
|
+
user: T | undefined;
|
|
332
|
+
parsed: T;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export type WithOptional<O extends object> = Expand<
|
|
336
|
+
{
|
|
337
|
+
[K in keyof O as O[K] extends HasDefault<any> ? never : K]: O[K];
|
|
338
|
+
} & {
|
|
339
|
+
[K in keyof O as O[K] extends HasDefault<any>
|
|
340
|
+
? K
|
|
341
|
+
: never]?: O[K] extends HasDefault<infer B> ? B : never;
|
|
342
|
+
}
|
|
343
|
+
>;
|
|
344
|
+
|
|
345
|
+
export type BuiltIn = Date | Function | Error | RegExp;
|
|
346
|
+
|
|
347
|
+
export type Primitive = string | number | boolean | symbol | null | undefined;
|
|
348
|
+
|
|
349
|
+
export type Expand<T> = T extends BuiltIn | Primitive
|
|
350
|
+
? T
|
|
351
|
+
: T extends object
|
|
352
|
+
? T extends infer O
|
|
353
|
+
? { [K in keyof O]: Expand<O[K]> }
|
|
354
|
+
: never
|
|
355
|
+
: T;
|
|
356
|
+
|
|
357
|
+
export type WithDefaults<O extends Object> = Expand<{
|
|
358
|
+
[K in keyof O]: O[K] extends HasDefault<infer B> ? B : O[K];
|
|
359
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function formatBytes(bytes: number) {
|
|
2
|
+
const units = ['bytes', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'];
|
|
3
|
+
let index = 0;
|
|
4
|
+
|
|
5
|
+
if (!bytes) return '0 bytes';
|
|
6
|
+
|
|
7
|
+
while (bytes >= 1024 && index < units.length - 1) {
|
|
8
|
+
bytes /= 1024;
|
|
9
|
+
index++;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return bytes.toFixed(2) + ' ' + units[index];
|
|
13
|
+
}
|