@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.
Files changed (128) hide show
  1. package/.env +2 -0
  2. package/.turbo/turbo-build.log +18 -0
  3. package/README.md +78 -0
  4. package/app/App.css +38 -0
  5. package/app/App.tsx +61 -0
  6. package/app/index.css +18 -0
  7. package/app/main.tsx +10 -0
  8. package/dist/components/StyleMe.d.ts +15 -0
  9. package/dist/components/StyleMe.d.ts.map +1 -0
  10. package/dist/components/error-boundary.d.ts +17 -0
  11. package/dist/components/error-boundary.d.ts.map +1 -0
  12. package/dist/components/explorer/edit-namespace-dialog.d.ts +14 -0
  13. package/dist/components/explorer/edit-namespace-dialog.d.ts.map +1 -0
  14. package/dist/components/explorer/edit-row-dialog.d.ts +10 -0
  15. package/dist/components/explorer/edit-row-dialog.d.ts.map +1 -0
  16. package/dist/components/explorer/expandable-deleted-attr.d.ts +15 -0
  17. package/dist/components/explorer/expandable-deleted-attr.d.ts.map +1 -0
  18. package/dist/components/explorer/explorer-layout.d.ts +8 -0
  19. package/dist/components/explorer/explorer-layout.d.ts.map +1 -0
  20. package/dist/components/explorer/index.d.ts +44 -0
  21. package/dist/components/explorer/index.d.ts.map +1 -0
  22. package/dist/components/explorer/inner-explorer.d.ts +16 -0
  23. package/dist/components/explorer/inner-explorer.d.ts.map +1 -0
  24. package/dist/components/explorer/new-namespace-dialog.d.ts +10 -0
  25. package/dist/components/explorer/new-namespace-dialog.d.ts.map +1 -0
  26. package/dist/components/explorer/query-inspector.d.ts +11 -0
  27. package/dist/components/explorer/query-inspector.d.ts.map +1 -0
  28. package/dist/components/explorer/recently-deleted.d.ts +36 -0
  29. package/dist/components/explorer/recently-deleted.d.ts.map +1 -0
  30. package/dist/components/explorer/search-input.d.ts +9 -0
  31. package/dist/components/explorer/search-input.d.ts.map +1 -0
  32. package/dist/components/explorer/table-components.d.ts +16 -0
  33. package/dist/components/explorer/table-components.d.ts.map +1 -0
  34. package/dist/components/explorer/view-settings.d.ts +10 -0
  35. package/dist/components/explorer/view-settings.d.ts.map +1 -0
  36. package/dist/components/rosePineDawnTheme.d.ts +13 -0
  37. package/dist/components/rosePineDawnTheme.d.ts.map +1 -0
  38. package/dist/components/select.d.ts +16 -0
  39. package/dist/components/select.d.ts.map +1 -0
  40. package/dist/components/toast.d.ts +4 -0
  41. package/dist/components/toast.d.ts.map +1 -0
  42. package/dist/components/ui.d.ts +336 -0
  43. package/dist/components/ui.d.ts.map +1 -0
  44. package/dist/config.d.ts +14 -0
  45. package/dist/config.d.ts.map +1 -0
  46. package/dist/hooks/explorer.d.ts +29 -0
  47. package/dist/hooks/explorer.d.ts.map +1 -0
  48. package/dist/hooks/useAttrNotes.d.ts +10 -0
  49. package/dist/hooks/useAttrNotes.d.ts.map +1 -0
  50. package/dist/hooks/useClickOutside.d.ts +3 -0
  51. package/dist/hooks/useClickOutside.d.ts.map +1 -0
  52. package/dist/hooks/useColumnVisibility.d.ts +12 -0
  53. package/dist/hooks/useColumnVisibility.d.ts.map +1 -0
  54. package/dist/hooks/useEditBlobConstraints.d.ts +32 -0
  55. package/dist/hooks/useEditBlobConstraints.d.ts.map +1 -0
  56. package/dist/hooks/useExplorerHistory.d.ts +1 -0
  57. package/dist/hooks/useExplorerHistory.d.ts.map +1 -0
  58. package/dist/hooks/useIsOverflow.d.ts +6 -0
  59. package/dist/hooks/useIsOverflow.d.ts.map +1 -0
  60. package/dist/hooks/useLocalStorage.d.ts +2 -0
  61. package/dist/hooks/useLocalStorage.d.ts.map +1 -0
  62. package/dist/hooks/useMonacoJSONSchema.d.ts +3 -0
  63. package/dist/hooks/useMonacoJSONSchema.d.ts.map +1 -0
  64. package/dist/hooks/useStableDB.d.ts +7 -0
  65. package/dist/hooks/useStableDB.d.ts.map +1 -0
  66. package/dist/index.cjs +15 -0
  67. package/dist/index.d.ts +7 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +9270 -0
  70. package/dist/schema.d.ts +5 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/style.css +1 -0
  73. package/dist/types.d.ts +241 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/utils/format.d.ts +2 -0
  76. package/dist/utils/format.d.ts.map +1 -0
  77. package/dist/utils/indexingJobs.d.ts +24 -0
  78. package/dist/utils/indexingJobs.d.ts.map +1 -0
  79. package/dist/utils/parsePermsJSON.d.ts +11 -0
  80. package/dist/utils/parsePermsJSON.d.ts.map +1 -0
  81. package/dist/utils/renames.d.ts +3 -0
  82. package/dist/utils/renames.d.ts.map +1 -0
  83. package/dist/utils/tableWidthSize.d.ts +9 -0
  84. package/dist/utils/tableWidthSize.d.ts.map +1 -0
  85. package/index.html +13 -0
  86. package/package.json +109 -0
  87. package/src/components/StyleMe.tsx +97 -0
  88. package/src/components/error-boundary.tsx +76 -0
  89. package/src/components/explorer/edit-namespace-dialog.tsx +1886 -0
  90. package/src/components/explorer/edit-row-dialog.tsx +1151 -0
  91. package/src/components/explorer/expandable-deleted-attr.tsx +170 -0
  92. package/src/components/explorer/explorer-layout.tsx +156 -0
  93. package/src/components/explorer/index.tsx +217 -0
  94. package/src/components/explorer/inner-explorer.tsx +1341 -0
  95. package/src/components/explorer/new-namespace-dialog.tsx +54 -0
  96. package/src/components/explorer/query-inspector.tsx +394 -0
  97. package/src/components/explorer/recently-deleted.tsx +344 -0
  98. package/src/components/explorer/search-input.tsx +358 -0
  99. package/src/components/explorer/table-components.tsx +341 -0
  100. package/src/components/explorer/view-settings.tsx +75 -0
  101. package/src/components/rosePineDawnTheme.ts +45 -0
  102. package/src/components/select.tsx +198 -0
  103. package/src/components/toast.tsx +18 -0
  104. package/src/components/ui.tsx +1561 -0
  105. package/src/config.ts +61 -0
  106. package/src/hooks/explorer.tsx +125 -0
  107. package/src/hooks/useAttrNotes.ts +27 -0
  108. package/src/hooks/useClickOutside.ts +23 -0
  109. package/src/hooks/useColumnVisibility.ts +39 -0
  110. package/src/hooks/useEditBlobConstraints.ts +185 -0
  111. package/src/hooks/useExplorerHistory.ts +0 -0
  112. package/src/hooks/useIsOverflow.ts +24 -0
  113. package/src/hooks/useLocalStorage.ts +51 -0
  114. package/src/hooks/useMonacoJSONSchema.ts +41 -0
  115. package/src/hooks/useStableDB.ts +30 -0
  116. package/src/index.tsx +8 -0
  117. package/src/schema.ts +285 -0
  118. package/src/style.css +5 -0
  119. package/src/types.ts +359 -0
  120. package/src/utils/format.ts +13 -0
  121. package/src/utils/indexingJobs.ts +126 -0
  122. package/src/utils/parsePermsJSON.ts +35 -0
  123. package/src/utils/renames.ts +42 -0
  124. package/src/utils/tableWidthSize.ts +62 -0
  125. package/tailwind.config.cjs +42 -0
  126. package/tsconfig.json +22 -0
  127. package/vite-env.d.ts +1 -0
  128. 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
@@ -0,0 +1,5 @@
1
+ @import 'tailwindcss';
2
+
3
+ @plugin '@tailwindcss/forms';
4
+
5
+ @custom-variant dark (&:where(.dark, .dark *));
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
+ }