@instantdb/platform 0.22.94 → 0.22.95-experimental.surgical.20385805945.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instantdb/platform",
3
- "version": "0.22.94",
3
+ "version": "0.22.95-experimental.surgical.20385805945.1",
4
4
  "description": "Instant's platform package for managing Instant apps.",
5
5
  "homepage": "https://github.com/instantdb/instant/tree/main/client/packages/platform",
6
6
  "repository": {
@@ -54,8 +54,8 @@
54
54
  "dependencies": {
55
55
  "@babel/parser": "^8.0.0-beta.0",
56
56
  "@babel/types": "^8.0.0-beta.0",
57
- "@instantdb/core": "0.22.94",
58
- "@instantdb/version": "0.22.94"
57
+ "@instantdb/core": "0.22.95-experimental.surgical.20385805945.1",
58
+ "@instantdb/version": "0.22.95-experimental.surgical.20385805945.1"
59
59
  },
60
60
  "scripts": {
61
61
  "test": "vitest",
package/src/index.ts CHANGED
@@ -56,3 +56,13 @@ export {
56
56
  type MigrationTxTypes,
57
57
  type Identifier,
58
58
  } from './migrations.ts';
59
+
60
+ export {
61
+ formatKey,
62
+ renderAttrCall,
63
+ renderAttrProperty,
64
+ renderEntityProperty,
65
+ renderLinkValue,
66
+ renderLinkProperty,
67
+ renderLinksObject,
68
+ } from './schemaCodegen.ts';
package/src/schema.ts CHANGED
@@ -15,9 +15,13 @@ import {
15
15
  indentLines,
16
16
  joinWithTrailingSep,
17
17
  sortedEntries,
18
- formatKey,
19
18
  GenericSchemaDef,
20
19
  } from './util.ts';
20
+ import {
21
+ formatKey,
22
+ renderEntityProperty,
23
+ renderLinksObject,
24
+ } from './schemaCodegen.ts';
21
25
 
22
26
  export type InstantAPIPlatformSchema = {
23
27
  refs: Record<string, InstantDBAttr>;
@@ -258,18 +262,6 @@ export type InstantAPISchemaPushStep =
258
262
  | InstantAPISchemaPushCheckDataTypeStep
259
263
  | InstantAPISchemaPushRemoveDataTypeStep;
260
264
 
261
- function attrDefToCodeString([name, attr]: [
262
- string,
263
- DataAttrDef<string, boolean, boolean>,
264
- ]) {
265
- const type =
266
- (attr.metadata.derivedType as any)?.type || attr.valueType || 'any';
267
- const unique = attr.config.unique ? '.unique()' : '';
268
- const index = attr.config.indexed ? '.indexed()' : '';
269
- const required = attr.required ? '' : '.optional()';
270
- return `${formatKey(name)}: i.${type}()${unique}${index}${required}`;
271
- }
272
-
273
265
  function entityDefToCodeStr(
274
266
  name: string,
275
267
  edef: EntityDef<
@@ -278,14 +270,10 @@ function entityDefToCodeStr(
278
270
  any
279
271
  >,
280
272
  ) {
281
- const attrBlock = joinWithTrailingSep(
282
- sortedEntries(edef.attrs).map(attrDefToCodeString),
283
- ',\n',
284
- ',',
285
- );
286
-
287
- // a block of code for each entity
288
- return `${formatKey(name)}: i.entity({${attrBlock.length ? '\n' : ''}${indentLines(attrBlock, 2)}${attrBlock.length ? '\n' : ''}})`;
273
+ return renderEntityProperty(name, edef.attrs, {
274
+ keyOptions: { alwaysQuote: true, quote: '"' },
275
+ trailingComma: true,
276
+ });
289
277
  }
290
278
 
291
279
  export function identEtype(ident: InstantDBIdent) {
@@ -338,7 +326,7 @@ function roomDefToCodeStr(room: RoomsDef[string]) {
338
326
  }
339
327
 
340
328
  if (room.topics) {
341
- ret += `\n "topics": {`;
329
+ ret += `\n ${formatKey('topics', { alwaysQuote: true, quote: '"' })}: {`;
342
330
 
343
331
  for (const [topicName, topicConfig] of Object.entries(room.topics)) {
344
332
  ret += `\n${indentLines(entityDefToCodeStr(topicName, topicConfig), 6)},`;
@@ -355,7 +343,7 @@ function roomsCodeStr(rooms: RoomsDef) {
355
343
  let ret = '{';
356
344
 
357
345
  for (const [roomType, roomDef] of Object.entries(rooms)) {
358
- ret += `\n ${formatKey(roomType)}: ${roomDefToCodeStr(roomDef)},`;
346
+ ret += `\n ${formatKey(roomType, { alwaysQuote: true, quote: '"' })}: ${roomDefToCodeStr(roomDef)},`;
359
347
  }
360
348
  ret += ret === '{' ? '}' : '\n}';
361
349
 
@@ -395,7 +383,11 @@ export function generateSchemaTypescriptFile(
395
383
  // run \`push schema\` again to enforce the types.`
396
384
  : '';
397
385
 
398
- const linksEntriesCode = JSON.stringify(newSchema.links, null, 2).trim();
386
+ const linksEntriesCode = renderLinksObject(newSchema.links, {
387
+ keyOptions: { alwaysQuote: true, quote: '"' },
388
+ stringQuote: '"',
389
+ style: 'json',
390
+ });
399
391
 
400
392
  // rooms
401
393
  const rooms = prevSchema?.rooms || {};
@@ -0,0 +1,186 @@
1
+ import type { AttrsDefs, DataAttrDef, LinkDef, LinksDef } from '@instantdb/core';
2
+ import { indentLines, joinWithTrailingSep, sortedEntries } from './util.ts';
3
+
4
+ const DEFAULT_INDENT = 2;
5
+
6
+ type AnyLink = LinkDef<any, any, any, any, any, any, any>;
7
+
8
+ type KeyFormatOptions = {
9
+ alwaysQuote?: boolean;
10
+ quote?: '"' | "'";
11
+ };
12
+
13
+ type EntityRenderOptions = {
14
+ indent?: number;
15
+ typeParamsByAttr?: Record<string, string | null | undefined>;
16
+ keyOptions?: KeyFormatOptions;
17
+ trailingComma?: boolean;
18
+ };
19
+
20
+ type LinkRenderOptions = {
21
+ indent?: number;
22
+ keyOptions?: KeyFormatOptions;
23
+ stringQuote?: '"' | "'";
24
+ style?: 'js' | 'json';
25
+ };
26
+
27
+ type LinksObjectOptions = {
28
+ indent?: number;
29
+ newlineForEmpty?: boolean;
30
+ keyOptions?: KeyFormatOptions;
31
+ stringQuote?: '"' | "'";
32
+ style?: 'js' | 'json';
33
+ };
34
+
35
+ export function formatKey(name: string, options: KeyFormatOptions = {}) {
36
+ const quote = options.quote ?? "'";
37
+ if (!options.alwaysQuote && isValidIdentifier(name)) {
38
+ return name;
39
+ }
40
+ return `${quote}${name}${quote}`;
41
+ }
42
+
43
+ export function renderAttrCall(
44
+ attr: DataAttrDef<string, boolean, boolean>,
45
+ typeParams?: string | null,
46
+ ) {
47
+ const type =
48
+ (attr.metadata?.derivedType as any)?.type || attr.valueType || 'any';
49
+ const unique = attr.config.unique ? '.unique()' : '';
50
+ const index = attr.config.indexed ? '.indexed()' : '';
51
+ const required = attr.required ? '' : '.optional()';
52
+ return `i.${type}${typeParams ?? ''}()${unique}${index}${required}`;
53
+ }
54
+
55
+ export function renderAttrProperty(
56
+ name: string,
57
+ attr: DataAttrDef<string, boolean, boolean>,
58
+ options: { typeParams?: string | null; keyOptions?: KeyFormatOptions } = {},
59
+ ) {
60
+ return `${formatKey(name, options.keyOptions)}: ${renderAttrCall(
61
+ attr,
62
+ options.typeParams,
63
+ )}`;
64
+ }
65
+
66
+ export function renderEntityProperty(
67
+ name: string,
68
+ attrs: AttrsDefs,
69
+ options: EntityRenderOptions = {},
70
+ ) {
71
+ const indent = options.indent ?? DEFAULT_INDENT;
72
+ const keyOptions = options.keyOptions;
73
+ const attrBlock = sortedEntries(attrs)
74
+ .map(([attrName, attrDef]) =>
75
+ renderAttrProperty(
76
+ attrName,
77
+ attrDef,
78
+ {
79
+ typeParams: options.typeParamsByAttr?.[attrName] ?? null,
80
+ keyOptions,
81
+ },
82
+ ),
83
+ )
84
+ .filter(Boolean);
85
+ const attrBlockText = options.trailingComma
86
+ ? joinWithTrailingSep(attrBlock, ',\n', ',')
87
+ : attrBlock.join(',\n');
88
+ const inner = attrBlockText.length
89
+ ? `\n${indentLines(attrBlockText, indent)}\n`
90
+ : '';
91
+ return `${formatKey(name, keyOptions)}: i.entity({${inner}})`;
92
+ }
93
+
94
+ export function renderLinkValue(
95
+ link: AnyLink,
96
+ options: LinkRenderOptions = {},
97
+ ) {
98
+ const indent = options.indent ?? DEFAULT_INDENT;
99
+ const indentStr = ' '.repeat(indent);
100
+ const keyOptions = options.keyOptions;
101
+ const q = options.stringQuote ?? "'";
102
+ const style = options.style ?? 'js';
103
+ const forwardLines = [
104
+ `${formatKey('on', keyOptions)}: ${q}${link.forward.on}${q}`,
105
+ `${formatKey('has', keyOptions)}: ${q}${link.forward.has}${q}`,
106
+ `${formatKey('label', keyOptions)}: ${q}${link.forward.label}${q}`,
107
+ ];
108
+ if (link.forward.required) {
109
+ forwardLines.push(`${formatKey('required', keyOptions)}: true`);
110
+ }
111
+ if (link.forward.onDelete) {
112
+ forwardLines.push(
113
+ `${formatKey('onDelete', keyOptions)}: ${q}${link.forward.onDelete}${q}`,
114
+ );
115
+ }
116
+
117
+ const reverseLines = [
118
+ `${formatKey('on', keyOptions)}: ${q}${link.reverse.on}${q}`,
119
+ `${formatKey('has', keyOptions)}: ${q}${link.reverse.has}${q}`,
120
+ `${formatKey('label', keyOptions)}: ${q}${link.reverse.label}${q}`,
121
+ ];
122
+ if (link.reverse.onDelete) {
123
+ reverseLines.push(
124
+ `${formatKey('onDelete', keyOptions)}: ${q}${link.reverse.onDelete}${q}`,
125
+ );
126
+ }
127
+
128
+ const forwardBodyLines =
129
+ style === 'json'
130
+ ? forwardLines
131
+ : forwardLines.map((line) => `${line},`);
132
+ const reverseBodyLines =
133
+ style === 'json'
134
+ ? reverseLines
135
+ : reverseLines.map((line) => `${line},`);
136
+
137
+ return [
138
+ '{',
139
+ `${indentStr}${formatKey('forward', keyOptions)}: {`,
140
+ `${indentStr}${indentStr}${forwardBodyLines.join(
141
+ `${style === 'json' ? ',' : ''}\n${indentStr}${indentStr}`,
142
+ )}`,
143
+ `${indentStr}}${style === 'json' ? ',' : ','}`,
144
+ `${indentStr}${formatKey('reverse', keyOptions)}: {`,
145
+ `${indentStr}${indentStr}${reverseBodyLines.join(
146
+ `${style === 'json' ? ',' : ''}\n${indentStr}${indentStr}`,
147
+ )}`,
148
+ `${indentStr}}${style === 'json' ? '' : ','}`,
149
+ '}',
150
+ ].join('\n');
151
+ }
152
+
153
+ export function renderLinkProperty(
154
+ name: string,
155
+ link: AnyLink,
156
+ options: LinkRenderOptions = {},
157
+ ) {
158
+ return `${formatKey(name, options.keyOptions)}: ${renderLinkValue(
159
+ link,
160
+ options,
161
+ )}`;
162
+ }
163
+
164
+ export function renderLinksObject(
165
+ links: LinksDef<any>,
166
+ options: LinksObjectOptions = {},
167
+ ) {
168
+ const indent = options.indent ?? DEFAULT_INDENT;
169
+ const entries = sortedEntries(links).map(([name, link]) =>
170
+ renderLinkProperty(name, link, {
171
+ indent,
172
+ keyOptions: options.keyOptions,
173
+ stringQuote: options.stringQuote,
174
+ style: options.style,
175
+ }),
176
+ );
177
+ if (!entries.length && !options.newlineForEmpty) {
178
+ return '{}';
179
+ }
180
+ const inner = entries.length ? indentLines(entries.join(',\n'), indent) : '';
181
+ return `{\n${inner}\n}`;
182
+ }
183
+
184
+ function isValidIdentifier(name: string) {
185
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
186
+ }