@tinacms/datalayer 0.2.0 → 0.2.3

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/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
@@ -30,5 +30,5 @@ export declare class FilesystemBridge implements Bridge {
30
30
  * Same as the `FileSystemBridge` except it does not save files
31
31
  */
32
32
  export declare class AuditFileSystemBridge extends FilesystemBridge {
33
- put(_filepath: string, _data: string): Promise<void>;
33
+ put(filepath: string, data: string): Promise<void>;
34
34
  }
@@ -140,5 +140,5 @@ export declare const makeFilterSuffixes: (filterChain: (BinaryFilter | TernaryFi
140
140
  left?: string;
141
141
  right?: string;
142
142
  } | undefined;
143
- export declare const makeKeyForField: (definition: IndexDefinition, data: object, stringEscaper: StringEscaper) => string | null;
143
+ export declare const makeKeyForField: (definition: IndexDefinition, data: object, stringEscaper: StringEscaper, maxStringLength?: number) => string | null;
144
144
  export {};
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ var __toModule = (module2) => {
51
51
  return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
52
52
  };
53
53
 
54
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/index.ts
54
+ // src/index.ts
55
55
  __export(exports, {
56
56
  AuditFileSystemBridge: () => AuditFileSystemBridge,
57
57
  AuditFilesystemStore: () => AuditFilesystemStore,
@@ -73,7 +73,7 @@ __export(exports, {
73
73
  makeStringEscaper: () => makeStringEscaper
74
74
  });
75
75
 
76
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/bridge/filesystem.ts
76
+ // src/database/bridge/filesystem.ts
77
77
  var import_fs_extra = __toModule(require("fs-extra"));
78
78
  var import_fast_glob = __toModule(require("fast-glob"));
79
79
  var import_path = __toModule(require("path"));
@@ -109,17 +109,25 @@ var FilesystemBridge = class {
109
109
  }
110
110
  };
111
111
  var AuditFileSystemBridge = class extends FilesystemBridge {
112
- async put(_filepath, _data) {
112
+ async put(filepath, data) {
113
+ if ([
114
+ ".tina/__generated__/_lookup.json",
115
+ ".tina/__generated__/_schema.json",
116
+ ".tina/__generated__/_graphql.json"
117
+ ].includes(filepath)) {
118
+ return super.put(filepath, data);
119
+ }
113
120
  return;
114
121
  }
115
122
  };
116
123
 
117
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/bridge/isomorphic.ts
124
+ // src/database/bridge/isomorphic.ts
118
125
  var import_isomorphic_git = __toModule(require("isomorphic-git"));
119
126
  var import_fs_extra2 = __toModule(require("fs-extra"));
120
127
  var import_glob_parent = __toModule(require("glob-parent"));
121
128
  var import_normalize_path2 = __toModule(require("normalize-path"));
122
129
  var import_graphql = __toModule(require("graphql"));
130
+ var import_path2 = __toModule(require("path"));
123
131
  var flat = typeof Array.prototype.flat === "undefined" ? (entries) => entries.reduce((acc, x) => acc.concat(x), []) : (entries) => entries.flat();
124
132
  var toUint8Array = (buf) => {
125
133
  const ab = new ArrayBuffer(buf.length);
@@ -208,9 +216,18 @@ var IsomorphicBridge = class {
208
216
  let pathParts = path4.split("/");
209
217
  const result = await import_isomorphic_git.default.walk(__spreadProps(__spreadValues({}, this.isomorphicConfig), {
210
218
  map: async (filepath, [head]) => {
211
- if (head._fullpath === "." || path4.startsWith(filepath)) {
219
+ if (head._fullpath === ".") {
212
220
  return head;
213
221
  }
222
+ if (path4.startsWith(filepath)) {
223
+ if ((0, import_path2.dirname)(path4) === (0, import_path2.dirname)(filepath)) {
224
+ if (path4 === filepath) {
225
+ return head;
226
+ }
227
+ } else {
228
+ return head;
229
+ }
230
+ }
214
231
  },
215
232
  cache: this.cache,
216
233
  trees: [import_isomorphic_git.default.TREE({ ref })]
@@ -431,16 +448,16 @@ var IsomorphicBridge = class {
431
448
  }
432
449
  };
433
450
 
434
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/store/filesystem.ts
451
+ // src/database/store/filesystem.ts
435
452
  var import_fs_extra3 = __toModule(require("fs-extra"));
436
453
  var import_fast_glob2 = __toModule(require("fast-glob"));
437
- var import_path2 = __toModule(require("path"));
454
+ var import_path3 = __toModule(require("path"));
438
455
  var import_normalize_path3 = __toModule(require("normalize-path"));
439
456
 
440
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/util.ts
457
+ // src/database/util.ts
441
458
  var import_gray_matter = __toModule(require("gray-matter"));
442
459
 
443
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/util.ts
460
+ // src/util.ts
444
461
  var yup = __toModule(require("yup"));
445
462
  var import_graphql2 = __toModule(require("graphql"));
446
463
  var sequential = async (items, callback) => {
@@ -479,7 +496,7 @@ var btoa = (string) => {
479
496
  return Buffer.from(string).toString("base64");
480
497
  };
481
498
 
482
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/util.ts
499
+ // src/database/util.ts
483
500
  var stringifyFile = (content, format, keepTemplateKey) => {
484
501
  switch (format) {
485
502
  case ".markdown":
@@ -534,7 +551,7 @@ var parseFile = (content, format, yupSchema) => {
534
551
  }
535
552
  };
536
553
 
537
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/store/filesystem.ts
554
+ // src/database/store/filesystem.ts
538
555
  var FilesystemStore = class {
539
556
  async clear() {
540
557
  }
@@ -550,7 +567,7 @@ var FilesystemStore = class {
550
567
  throw new Error(`Seeding data is not possible for Filesystem store`);
551
568
  }
552
569
  async get(filepath) {
553
- return parseFile(await import_fs_extra3.default.readFileSync(import_path2.default.join(this.rootPath, filepath)).toString(), import_path2.default.extname(filepath), (yup2) => yup2.object());
570
+ return parseFile(await import_fs_extra3.default.readFileSync(import_path3.default.join(this.rootPath, filepath)).toString(), import_path3.default.extname(filepath), (yup2) => yup2.object());
554
571
  }
555
572
  supportsSeeding() {
556
573
  return false;
@@ -559,8 +576,8 @@ var FilesystemStore = class {
559
576
  return false;
560
577
  }
561
578
  async glob(pattern, callback, extension) {
562
- const basePath = import_path2.default.join(this.rootPath, ...pattern.split("/"));
563
- const itemsRaw = await (0, import_fast_glob2.default)(import_path2.default.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"), {
579
+ const basePath = import_path3.default.join(this.rootPath, ...pattern.split("/"));
580
+ const itemsRaw = await (0, import_fast_glob2.default)(import_path3.default.join(basePath, "**", `/*${extension}`).replace(/\\/g, "/"), {
564
581
  dot: true
565
582
  });
566
583
  const posixRootPath = (0, import_normalize_path3.default)(this.rootPath);
@@ -576,14 +593,14 @@ var FilesystemStore = class {
576
593
  }
577
594
  }
578
595
  async put(filepath, data, options) {
579
- await import_fs_extra3.default.outputFileSync(import_path2.default.join(this.rootPath, filepath), stringifyFile(data, import_path2.default.extname(filepath), options.keepTemplateKey));
596
+ await import_fs_extra3.default.outputFileSync(import_path3.default.join(this.rootPath, filepath), stringifyFile(data, import_path3.default.extname(filepath), options.keepTemplateKey));
580
597
  }
581
598
  async open() {
582
599
  }
583
600
  async close() {
584
601
  }
585
602
  async delete(filepath) {
586
- await import_fs_extra3.default.remove(import_path2.default.join(this.rootPath, filepath));
603
+ await import_fs_extra3.default.remove(import_path3.default.join(this.rootPath, filepath));
587
604
  }
588
605
  };
589
606
  var AuditFilesystemStore = class extends FilesystemStore {
@@ -592,7 +609,7 @@ var AuditFilesystemStore = class extends FilesystemStore {
592
609
  }
593
610
  };
594
611
 
595
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/store/index.ts
612
+ // src/database/store/index.ts
596
613
  var import_jsonpath_plus = __toModule(require("jsonpath-plus"));
597
614
  var DEFAULT_COLLECTION_SORT_KEY = "__filepath__";
598
615
  var INDEX_KEY_FIELD_SEPARATOR = "#";
@@ -921,11 +938,12 @@ var makeFilterSuffixes = (filterChain, index) => {
921
938
  return {};
922
939
  }
923
940
  };
924
- var makeKeyForField = (definition, data, stringEscaper) => {
941
+ var makeKeyForField = (definition, data, stringEscaper, maxStringLength = 100) => {
925
942
  const valueParts = [];
926
943
  for (const field of definition.fields) {
927
- if (field.name in data) {
928
- const resolvedValue = String(field.type === "datetime" ? new Date(data[field.name]).getTime() : field.type === "string" ? stringEscaper(data[field.name]) : data[field.name]);
944
+ if (field.name in data && data[field.name] !== void 0 && data[field.name] !== null) {
945
+ const rawValue = data[field.name];
946
+ const resolvedValue = String(field.type === "datetime" ? new Date(rawValue).getTime() : field.type === "string" ? stringEscaper(rawValue) : rawValue).substring(0, maxStringLength);
929
947
  valueParts.push(applyPadding(resolvedValue, field.pad));
930
948
  } else {
931
949
  return null;
@@ -934,8 +952,8 @@ var makeKeyForField = (definition, data, stringEscaper) => {
934
952
  return valueParts.join(INDEX_KEY_FIELD_SEPARATOR);
935
953
  };
936
954
 
937
- // pnp:/home/runner/work/tinacms/tinacms/packages/@tinacms/datalayer/src/database/store/level.ts
938
- var import_path3 = __toModule(require("path"));
955
+ // src/database/store/level.ts
956
+ var import_path4 = __toModule(require("path"));
939
957
  var import_level = __toModule(require("level"));
940
958
  var import_levelup = __toModule(require("levelup"));
941
959
  var import_memdown = __toModule(require("memdown"));
@@ -949,7 +967,7 @@ var LevelStore = class {
949
967
  if (useMemory) {
950
968
  this.db = (0, import_levelup.default)((0, import_encoding_down.default)((0, import_memdown.default)(), { valueEncoding: "json" }));
951
969
  } else {
952
- this.db = (0, import_level.default)(import_path3.default.join(rootPath, ".tina/__generated__/db"), {
970
+ this.db = (0, import_level.default)(import_path4.default.join(rootPath, ".tina/__generated__/db"), {
953
971
  valueEncoding: "json"
954
972
  });
955
973
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/datalayer",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
6
6
  "files": [
@@ -17,11 +17,6 @@
17
17
  }
18
18
  ]
19
19
  },
20
- "scripts": {
21
- "types": "yarn tsc",
22
- "build": "echo \"Run `yarn build` from the root of the repository instead\"",
23
- "test": "jest --passWithNoTests"
24
- },
25
20
  "dependencies": {
26
21
  "@octokit/auth-app": "^2.6.0",
27
22
  "@octokit/graphql": "^4.5.6",
@@ -52,7 +47,7 @@
52
47
  "directory": "packages/@tinacms/datalayer"
53
48
  },
54
49
  "devDependencies": {
55
- "@tinacms/scripts": "0.50.9",
50
+ "@tinacms/scripts": "0.51.1",
56
51
  "@types/fs-extra": "^9.0.2",
57
52
  "@types/jest": "^27.4.1",
58
53
  "@types/js-yaml": "^3.12.5",
@@ -67,6 +62,11 @@
67
62
  "jest-diff": "27.0.6",
68
63
  "jest-file-snapshot": "^0.5.0",
69
64
  "jest-matcher-utils": "27.0.6",
70
- "typescript": "^4.3.5"
65
+ "typescript": "4.3.5"
66
+ },
67
+ "scripts": {
68
+ "types": "pnpm tsc",
69
+ "build": "tinacms-scripts build",
70
+ "test": "jest --passWithNoTests"
71
71
  }
72
72
  }
package/CHANGELOG.md DELETED
@@ -1,864 +0,0 @@
1
- # tina-graphql
2
-
3
- ## 0.2.0
4
-
5
- ### Minor Changes
6
-
7
- - 4daf15b36: Updated matching logic to only return the correct extension.
8
-
9
- This means if you are using any other files besides `.md` the format must be provided in the schema.
10
-
11
- ```ts
12
- // .tina/schema.ts
13
-
14
- import { defineSchema } from 'tinacms'
15
-
16
- const schema = defineSchema({
17
- collections: [
18
- {
19
- name: 'page',
20
- path: 'content/page',
21
- label: 'Page',
22
- // Need to provide the format if the file being used (default is `.md`)
23
- format: 'mdx',
24
- fields: [
25
- //...
26
- ],
27
- },
28
- ],
29
- })
30
- //...
31
-
32
- export default schema
33
- ```
34
-
35
- ### Patch Changes
36
-
37
- - b348f8b6b: Experimental isomorphic git bridge implementation
38
-
39
- ## 0.1.1
40
-
41
- ### Patch Changes
42
-
43
- - a2906d6fe: Fix datetime filtering to handle both indexed and non-indexed queries
44
- - 3e2d9e43a: Adds new GraphQL `deleteDocument` mutation and logic
45
-
46
- ## 0.1.0
47
-
48
- ### Minor Changes
49
-
50
- - a87e1e6fa: Enable query filtering, pagination, sorting
51
-
52
- ### Patch Changes
53
-
54
- - 8b3be903f: Escape index field separator in input strings
55
- - b01f2e382: Fixed an issue where `0` as a numerical operand was being evaluated as falsy.
56
-
57
- ## 0.0.2
58
-
59
- ### Patch Changes
60
-
61
- - b399c734c: Fixes support for collection.templates in graphql
62
-
63
- ## 0.0.1
64
-
65
- ### Patch Changes
66
-
67
- - 80732bd97: Create a @tinacms/datalayer package which houses the logic for data management for the GraphQL API. This simplifies the @tinacms/graphql package and allows for a clearer separation.
68
-
69
- ## 0.59.1
70
-
71
- ### Patch Changes
72
-
73
- - f46c6f987: Fix type definitions for schema metadata so they're optional
74
-
75
- ## 0.59.0
76
-
77
- ### Minor Changes
78
-
79
- - 62bea7019: #2323: fix saving bold and italic text in rich-text editor
80
-
81
- ### Patch Changes
82
-
83
- - bd4e1f802: Pin version number from @tinacms/graphql during schema compilation. This can be used to ensure the proper version is provided when working with Tina Cloud
84
-
85
- ## 0.58.2
86
-
87
- ### Patch Changes
88
-
89
- - fffce3af8: Don't cache graphql schema during resolution, this was causing the schema to go stale, while updating the schema.gql, so GraphQL tooling thought the value was updated, but the server was still holding on to the cached version
90
-
91
- ## 0.58.1
92
-
93
- ### Patch Changes
94
-
95
- - 4700d7ae4: Patch fix to ensure builds include latest dependencies
96
-
97
- ## 0.58.0
98
-
99
- ### Minor Changes
100
-
101
- - fa7a0419f: Adds experimental support for a data layer between file-based content and the GraphQL API. This allows documents to be indexed so the CMS can behave more like a traditional CMS, with the ability enforce foreign reference constraints and filtering/pagination capabilities.
102
-
103
- ### Patch Changes
104
-
105
- - eb5fbfac7: Ensure GraphQL resolve doesn't access "bridge" documents
106
- - 47d126029: Fix support of objects in a list for MDX templates
107
-
108
- ## 0.57.2
109
-
110
- ### Patch Changes
111
-
112
- - edb2f4011: Trim path property on collections during compilation
113
-
114
- ## 0.57.1
115
-
116
- ### Patch Changes
117
-
118
- - 60729f60c: Adds a `reference` field
119
-
120
- ## 0.57.0
121
-
122
- ### Minor Changes
123
-
124
- - ed277e3bd: Remove aws dependency and cache logic from GithubBridge
125
- - d1ed404ba: Add support for auto-generated SDK for type-safe data fetching
126
-
127
- ### Patch Changes
128
-
129
- - 138ceb8c4: Clean up dependencies
130
- - 577d6a5ad: Adds collection arg back for generic queries as optional
131
-
132
- ## 0.56.1
133
-
134
- ### Patch Changes
135
-
136
- - 4b7795612: Adds support for collection.templates to TinaAdmin
137
-
138
- ## 0.56.0
139
-
140
- ### Minor Changes
141
-
142
- - b99baebf1: Add rich-text editor based on mdx, bump React dependency requirement to 16.14
143
-
144
- ### Patch Changes
145
-
146
- - 891623c7c: Adds support for List and Update to TinaAdmin
147
-
148
- ## 0.55.2
149
-
150
- ### Patch Changes
151
-
152
- - 9ecb392ca: Fix bug which would set markdown body to undefined when the payload was emptry"
153
-
154
- ## 0.55.1
155
-
156
- ### Patch Changes
157
-
158
- - ff4446c8e: Adds `getDocumentFields()` query for use with Tina Admin
159
- - 667c33e2a: Add support for rich-text field, update build script to work with unified packages, which are ESM-only
160
-
161
- ## 0.55.0
162
-
163
- ### Minor Changes
164
-
165
- - f3bddeb4a: Added new warning messages for list UI that we do not support by default
166
-
167
- ### Patch Changes
168
-
169
- - 2908f8176: Fixes an issue where nested reference fields weren't updated properly when their values changed.
170
- - 5d83643b2: Adds create document mutations
171
-
172
- ## 0.54.3
173
-
174
- ### Patch Changes
175
-
176
- - 9b27192fe: Build packages with new scripting, which includes preliminary support for ES modules.
177
-
178
- ## 0.54.2
179
-
180
- ### Patch Changes
181
-
182
- - d94fec611: Improve exported types for defineSchema
183
-
184
- ## 0.54.1
185
-
186
- ### Patch Changes
187
-
188
- - 4de977f63: Makes `DateFieldPlugin` timezone-friendly
189
-
190
- ## 0.54.0
191
-
192
- ### Minor Changes
193
-
194
- - 7663e0f7f: Fixed windows issue where you could not save a file
195
-
196
- ## 0.53.0
197
-
198
- ### Minor Changes
199
-
200
- - b4f5e973f: Update datetime field to expect and receive ISO string
201
-
202
- ## 0.52.2
203
-
204
- ### Patch Changes
205
-
206
- - b4bbdda86: Better error messaging when no tina schema files are found
207
-
208
- ## 0.52.1
209
-
210
- ### Patch Changes
211
-
212
- - b05c91c6: Remove console.log
213
-
214
- ## 0.52.0
215
-
216
- ### Minor Changes
217
-
218
- - aa4507697: When working with a new document that queries for a reference, we were not properly building the path information required to update that reference, resulting in an error until the page was refreshed.
219
-
220
- ## 0.51.1
221
-
222
- ### Patch Changes
223
-
224
- - 589c7806: Fix issue where the `isBody` field wasn't properly removing that value from frontmatter. Ensure that the field is not treating any differently for JSON format
225
-
226
- ## 0.51.0
227
-
228
- ### Minor Changes
229
-
230
- - 5a934f6b: Fixed windows path issues
231
-
232
- ### Patch Changes
233
-
234
- - 271a72d7: Use collection label (defined in schema.ts) as form label
235
-
236
- ## 0.50.2
237
-
238
- ### Patch Changes
239
-
240
- - 0970961f: addPendingDocument was expecting params, which are not supported for new doc creation at this time
241
-
242
- ## 0.50.1
243
-
244
- ### Patch Changes
245
-
246
- - 65b3e3a3: Uses checkbox-group field
247
-
248
- ## 0.50.0
249
-
250
- ### Minor Changes
251
-
252
- - 7f3c8c1a: # 🔧 Changes coming to TinaCMS ⚙️
253
-
254
- 👋 You may have noticed we've been hard at-work lately building out a more opinionated approach to TinaCMS. To that end, we've settled around a few key points we'd like to announce. To see the work in progress, check out the [main](https://github.com/tinacms/tinacms/tree/main) branch, which will become the primary branch soon.
255
-
256
- ## Consolidating @tinacms packages in to @tinacms/toolkit
257
-
258
- By nature, Tina relies heavily on React context, and the dependency mismatches from over-modularizing our toolkit has led to many bugs related to missing context. To fix this, we'll be consolidating nearly every package in the @tinacms scope to a single package called `@tinacms/toolkit`
259
-
260
- We'll also be rolling out esm support as it's now much easier to address build improvements
261
-
262
- ## A more focused tinacms package
263
-
264
- The `tinacms` package now comes baked-in with APIs for working with the TinaCMS GraphQL API. Because `@tinacms/toolkit` now encompasses everything you'd need to build your own CMS integration, we're repurposing the `tinacms` package to more accurately reflect the "batteries-included" approach.
265
-
266
- If you haven't been introduced, the GraphQL API is a Git-backed CMS which we'll be leaning into more in the future. With a generous free tier and direct syncing with Github its something we're really excited to push forward. Sign up for free here
267
- Note: tinacms still exports the same APIs, but we'll gradually start moving the backend-agnostic tools to @tinacms/toolkit.
268
-
269
- ## Consolidating the tina-graphql-gateway repo
270
-
271
- The tina-graphql-gateway repo will be absorbed into this one. If you've been working with our GraphQL APIs you'll need to follow our migration guide.
272
-
273
- ## Moving from Lerna to Yarn PNP
274
-
275
- We've had success with Yarn 2 and PNP in other monorepos, if you're a contributor you'll notice some updates to the DX, which should hopefully result in a smoother experience.
276
-
277
- ## FAQ
278
-
279
- ### What about other backends?
280
-
281
- The `@tinacms/toolkit` isn't going anywhere. And if you're using packages like `react-tinacms-strapi` or r`eact-tinacms-github` with success, that won't change much, they'll just be powered by `@tinacms/toolkit` under the hood.
282
-
283
- ### Do I need to do anything?
284
-
285
- We'll be bumping all packages to `0.50.0` to reflect the changes. If you're using @tincams scoped packages those won't receive the upgrade. Unscoped packages like `react-tinacms-editor` will be upgraded, and should be bumped to 0.50.0 as well.
286
- When we move to `1.0.0` we'll be pushing internal APIs to `@tinacms/toolkit`, so that's the long-term location of
287
-
288
- ### Will you continue to patch older versions?
289
-
290
- We'll continue to make security patches, however major bug fixes will likely not see any updates. Keep in mind that `@tinacms/toolkit` will continue to be developed.
291
-
292
- ## 0.2.0
293
-
294
- ### Minor Changes
295
-
296
- - 7351d92f: # Define schema changes
297
-
298
- We're going to be leaning on a more _primitive_ concept of how types are defined with Tina, and in doing so will be introducing some breaking changes to the way schemas are defined. Read the detailed [RFC discussion](https://github.com/tinacms/rfcs/pull/18) for more on this topic, specifically the [latter portions](https://github.com/tinacms/rfcs/pull/18#issuecomment-805400313) of the discussion.
299
-
300
- ## Collections now accept a `fields` _or_ `templates` property
301
-
302
- You can now provide `fields` instead of `templates` for your collection, doing so will result in a more straightforward schema definition:
303
-
304
- ```js
305
- {
306
- collections: [
307
- {
308
- name: 'post',
309
- label: 'Post',
310
- path: 'content/posts',
311
- fields: [
312
- {
313
- name: 'title',
314
- label: 'Title',
315
- type: 'string', // read on below to learn more about _type_ changes
316
- },
317
- ],
318
- // defining `fields` and `templates` would result in a compilation error
319
- },
320
- ]
321
- }
322
- ```
323
-
324
- **Why?**
325
-
326
- Previously, a collection could define multiple templates, the ambiguity introduced with this feature meant that your documents needed a `_template` field on them so we'd know which one they belonged to. It also mean having to disambiguate your queries in graphql:
327
-
328
- ```graphql
329
- getPostDocument(relativePage: $relativePath) {
330
- data {
331
- ...on Article_Doc_Data {
332
- title
333
- }
334
- }
335
- }
336
- ```
337
-
338
- Going forward, if you use `fields` on a collection, you can omit the `_template` key and simplify your query:
339
-
340
- ```graphql
341
- getPostDocument(relativePage: $relativePath) {
342
- data {
343
- title
344
- }
345
- }
346
- ```
347
-
348
- ## `type` changes
349
-
350
- Types will look a little bit different, and are meant to reflect the lowest form of the shape they can represent. Moving forward, the `ui` field will represent the UI portion of what you might expect. For a blog post "description" field, you'd define it like this:
351
-
352
- ```js
353
- {
354
- type: "string",
355
- label: "Description",
356
- name: "description",
357
- }
358
- ```
359
-
360
- By default `string` will use the `text` field, but you can change that by specifying the `component`:
361
-
362
- ```js
363
- {
364
- type: "string",
365
- label: "Description",
366
- name: "description",
367
- ui: {
368
- component: "textarea"
369
- }
370
- }
371
- ```
372
-
373
- For the most part, the UI properties are added to the field and adhere to the existing capabilities of Tina's core [field plugins](https://tina.io/docs/fields/). But there's nothing stopping you from providing your own components -- just be sure to register those with the CMS object on the frontend:
374
-
375
- ```js
376
- {
377
- type: "string",
378
- label: "Description",
379
- name: "description",
380
- ui: {
381
- component: "myMapField"
382
- someAdditionalMapConfig: 'some-value'
383
- }
384
- }
385
- ```
386
-
387
- [Register](https://tina.io/docs/fields/custom-fields/#registering-the-plugin) your `myMapField` with Tina:
388
-
389
- ```js
390
- cms.fields.add({
391
- name: 'myMapField',
392
- Component: MapPicker,
393
- })
394
- ```
395
-
396
- ### One important gotcha
397
-
398
- Every property in the `defineSchema` API must be serlializable. Meaning functions will not work. For example, there's no way to define a `validate` or `parse` function at this level. However, you can either use the [formify](https://tina.io/docs/tina-cloud/client/#formify) API to get access to the Tina form, or provide your own logic by specifying a plugin of your choice:
399
-
400
- ```js
401
- {
402
- type: "string",
403
- label: "Description",
404
- name: "description",
405
- ui: {
406
- component: "myText"
407
- }
408
- }
409
- ```
410
-
411
- And then when you register the plugin, provide your custom logic here:
412
-
413
- ```js
414
- import { TextFieldPlugin } from 'tinacms'
415
-
416
- // ...
417
-
418
- cms.fields.add({
419
- ...TextFieldPlugin, // spread existing text plugin
420
- name: 'myText',
421
- validate: value => {
422
- someValidationLogic(value)
423
- },
424
- })
425
- ```
426
-
427
- **Why?**
428
-
429
- The reality is that under the hood this has made no difference to the backend, so we're removing it as a point of friction. Instead, `type` is the true definition of the field's _shape_, while `ui` can be used for customizing the look and behavior of the field's UI.
430
-
431
- ## Defensive coding in Tina
432
-
433
- When working with GraphQL, there are 2 reasons a property may not be present.
434
-
435
- 1. The data is not a required property. That is to say, if I have a blog post document, and "category" is an optional field, we'll need to make sure we factor that into how we render our page:
436
-
437
- ```tsx
438
- const MyPage = props => {
439
- return (
440
- <>
441
- <h2>{props.getPostDocument.data.title}</h2>
442
- <MyCategoryComponent>
443
- {props.getPostDocument.data?.category}
444
- </MyCategoryComponent>
445
- </>
446
- )
447
- }
448
- ```
449
-
450
- 2. The query did not ask for that field:
451
-
452
- ```graphql
453
- {
454
- getPostDocument {
455
- data {
456
- title
457
- }
458
- }
459
- }
460
- ```
461
-
462
- But with Tina, there's a 3rd scenario: the document may be in an invalid state. Meaning, we could mark the field as `required` _and_ query for the appropriate field, and _still_ not have the expected shape of data. Due to the contextual nature of Tina, it's very common to be in an intermediate state, where your data is incomplete simply because you're still working on it. Most APIs would throw an error when a document is in an invalid state. Or, more likely, you couldn't even request it.
463
-
464
- ## Undefined list fields will return `null`
465
-
466
- Previously an listable field which wasn't defined in the document was treated as an emptry array. So for example:
467
-
468
- ```md
469
- ---
470
- title: 'Hello, World'
471
- categories:
472
- - sports
473
- - movies
474
- ---
475
- ```
476
-
477
- The responsee would be `categories: ['sports', 'movies']`. If you omit the items, but kept the empty array:
478
-
479
- ```md
480
- ---
481
- title: 'Hello, World'
482
- categories: []
483
- ---
484
- ```
485
-
486
- The responsee would be `categories: []`. If you omit the field entirely:
487
-
488
- ```md
489
- ---
490
- title: 'Hello, World'
491
- ---
492
- ```
493
-
494
- The response will be `categories: null`. Previously this would have been `[]`, which was incorrect.
495
-
496
- ## For a listable item which is `required: true` you _must_ provide a `ui.defaultItem` property
497
-
498
- ### Why?
499
-
500
- It's possible for Tina's editing capabilities to introduce an invalid state during edits to list items. Imagine the scenario where you are iterating through an array of objects, and each object has a categories array on it we'd like to render:
501
-
502
- ```tsx
503
- const MyPage = props => {
504
- return props.blocks.map(block => {
505
- return (
506
- <>
507
- <h2>{block.categories.split(',')}</h2>
508
- </>
509
- )
510
- })
511
- }
512
- ```
513
-
514
- For a new item, `categories` will be null, so we'll get an error. This only happens when you're editing your page with Tina, so it's not a production-facing issue.
515
-
516
- ## Every `type` can be a list
517
-
518
- Previously, we had a `list` field, which allowed you to supply a `field` property. Instead, _every_ primitive type can be represented as a list:
519
-
520
- ```js
521
- {
522
- type: "string",
523
- label: "Categories",
524
- name: "categories",
525
- list: true
526
- }
527
- ```
528
-
529
- Additionally, enumerable lists and selects are inferred from the `options` property. The following example is represented by a `select` field:
530
-
531
- ```js
532
- {
533
- type: "string",
534
- label: "Categories",
535
- name: "categories",
536
- options: ["fitness", "movies", "music"]
537
- }
538
- ```
539
-
540
- While this, is a `checkbox` field
541
-
542
- ```js
543
- {
544
- type: "string",
545
- label: "Categories",
546
- name: "categories"
547
- list: true,
548
- options: ["fitness", "movies", "music"]
549
- }
550
- ```
551
-
552
- > Note we may introduce an `enum` type, but haven't discussed it thoroughly
553
-
554
- ## Introducing the `object` type
555
-
556
- Tina currently represents the concept of an _object_ in two ways: a `group` (and `group-list`), which is a uniform collection of fields; and `blocks`, which is a polymporphic collection. Moving forward, we'll be introducing a more comporehensive type, which envelopes the behavior of both `group` and `blocks`, and since _every_ field can be a `list`, this also makes `group-list` redundant.
557
-
558
- > Note: we've previously assumed that `blocks` usage would _always_ be as an array. We'll be keeping that assumption with the `blocks` type for compatibility, but `object` will allow for non-array polymorphic objects.
559
-
560
- ### Defining an `object` type
561
-
562
- An `object` type takes either a `fields` _or_ `templates` property (just like the `collections` definition). If you supply `fields`, you'll end up with what is essentially a `group` item. And if you say `list: true`, you'll have what used to be a `group-list` definition.
563
-
564
- Likewise, if you supply a `templates` field and `list: true`, you'll get the same API as `blocks`. However you can also say `list: false` (or omit it entirely), and you'll have a polymorphic object which is _not_ an array.
565
-
566
- This is identical to the current `blocks` definition:
567
-
568
- ```js
569
- {
570
- type: "object",
571
- label: "Page Sections",
572
- name: "pageSections",
573
- list: true,
574
- templates: [{
575
- label: "Hero",
576
- name: "hero",
577
- fields: [{
578
- label: "Title",
579
- name: "title",
580
- type: "string"
581
- }]
582
- }]
583
- }
584
- ```
585
-
586
- And here is one for `group`:
587
-
588
- ```js
589
- {
590
- type: "object",
591
- label: "Hero",
592
- name: "hero",
593
- fields: [{
594
- label: "Title",
595
- name: "title",
596
- type: "string"
597
- }]
598
- }
599
- ```
600
-
601
- ## `dataJSON` field
602
-
603
- You can now request `dataJSON` for the entire data object as a single query key. This is great for more tedius queries like theme files where including each item in the result is cumbersome.
604
-
605
- > Note there is no typescript help for this feature for now
606
-
607
- ```graphql
608
- getThemeDocument(relativePath: $relativePath) {
609
- dataJSON
610
- }
611
- ```
612
-
613
- ```json
614
- {
615
- "getThemeDocument": {
616
- "dataJSON": {
617
- "every": "field",
618
- "in": {
619
- "the": "document"
620
- },
621
- "is": "returned"
622
- }
623
- }
624
- }
625
- ```
626
-
627
- ## Lists queries will now adhere to the GraphQL connection spec
628
-
629
- [Read the spec](https://relay.dev/graphql/connections.htm)
630
-
631
- Previously, lists would return a simple array of items:
632
-
633
- ```graphql
634
- {
635
- getPostsList {
636
- id
637
- }
638
- }
639
- ```
640
-
641
- Which would result in:
642
-
643
- ```json
644
- {
645
- "data": {
646
- "getPostsList": [
647
- {
648
- "id": "content/posts/voteForPedro.md"
649
- }
650
- ]
651
- }
652
- }
653
- ```
654
-
655
- In the new API, you'll need to step through `edges` & `nodes`:
656
-
657
- ```graphql
658
- {
659
- getPostsList {
660
- edges {
661
- node {
662
- id
663
- }
664
- }
665
- }
666
- }
667
- ```
668
-
669
- ```json
670
- {
671
- "data": {
672
- "getPostsList": {
673
- "edges": [
674
- {
675
- "node": {
676
- "id": "content/posts/voteForPedro.md"
677
- }
678
- }
679
- ]
680
- }
681
- }
682
- }
683
- ```
684
-
685
- **Why?**
686
-
687
- The GraphQL connection spec opens up a more future-proof structure, allowing us to put more information in to the _connection_ itself like how many results have been returned, and how to request the next page of data.
688
-
689
- Read [a detailed explanation](https://graphql.org/learn/pagination/) of how the connection spec provides a richer set of capabilities.
690
-
691
- > Note: sorting and filtering is still not supported for list queries.
692
-
693
- ## `_body` is no longer included by default
694
-
695
- There is instead an `isBody` boolean which can be added to any `string` field
696
-
697
- **Why?**
698
-
699
- Since markdown files sort of have an implicit "body" to them, we were automatically populating a field which represented the body of your markdown file. This wasn't that useful, and kind of annoying. Instead, just attach `isBody` to the field which you want to represent your markdown "body":
700
-
701
- ```js
702
- {
703
- collections: [{
704
- name: "post",
705
- label: "Post",
706
- path: "content/posts",
707
- fields: [
708
- {
709
- name: "title",
710
- label: "Title",
711
- type: "string"
712
- }
713
- {
714
- name: "myBody",
715
- label: "My Body",
716
- type: "string",
717
- component: 'textarea',
718
- isBody: true
719
- }
720
- ]
721
- }]
722
- }
723
- ```
724
-
725
- This would result in a form field called `My Body` getting saved to the body of your markdown file (if you're using markdown):
726
-
727
- ```md
728
- ---
729
- title: Hello, World!
730
- ---
731
-
732
- This is the body of the file, it's edited through the "My Body" field in your form.
733
- ```
734
-
735
- ## References now point to more than one collection.
736
-
737
- Instead of a `collection` property, you must now define a `collections` field, which is an array:
738
-
739
- ```js
740
- {
741
- type: "reference",
742
- label: "Author",
743
- name: "author",
744
- collections: ["author"]
745
- }
746
- ```
747
-
748
- ```graphql
749
- {
750
- getPostDocument(relativePath: "hello.md") {
751
- data {
752
- title
753
- author {
754
- ...on Author_Document {
755
- name
756
- }
757
- ...on Post_Document {
758
- title
759
- }
760
- }
761
- }
762
- }
763
- ```
764
-
765
- ## Other breaking changes
766
-
767
- ### The `template` field on polymorphic objects (formerly _blocks_) is now `_template`
768
-
769
- **Old API:**
770
-
771
- ```md
772
- ---
773
- ---
774
- myBlocks:
775
- - template: hero
776
- title: Hello
777
- ---
778
- ```
779
-
780
- **New API:**
781
-
782
- ```md
783
- ---
784
- ---
785
- myBlocks:
786
- - \_template: hero
787
- title: Hello
788
- ---
789
- ```
790
-
791
- ### `data` `__typename` values have changed
792
-
793
- They now include the proper namespace to prevent naming collisions and no longer require `_Doc_Data` suffix. All generated `__typename` properties are going to be slightly different. We weren't fully namespacing fields so it wasn't possible to guarantee that no collisions would occur. The pain felt here will likely be most seen when querying and filtering through blocks. This ensures the stability of this type in the future
794
-
795
- ```graphql
796
- {
797
- getPageDocument(relativePath: "home.md") {
798
- data {
799
- title
800
- myBlocks {
801
- ...on Page_Hero_Data { # previously this would have been Hero_Data
802
- # ...
803
- }
804
- }
805
- }
806
- }
807
- ```
808
-
809
- ### Patch Changes
810
-
811
- - fdb7724b: Fix stringify for json extensions
812
- - d42e2bcf: Adds number, datetime, and boolean fields back into primitive field generators
813
- - 5cd5ce76: - Improve types for ui field
814
- - Marks system fields as required so the user has a guarantee that they'll be there
815
- - Return null for listable fields which are null or undefined
816
- - Handle null values for reference fields better
817
- - 8c425440: Remmove accidental additional of dataJSON in schema
818
- - Updated dependencies [7351d92f]
819
- - tina-graphql-helpers@0.1.2
820
-
821
- ## 0.1.25
822
-
823
- ### Patch Changes
824
-
825
- - 348ef1e5: Testing version bumps go into a PR
826
-
827
- ## 0.1.24
828
-
829
- ### Patch Changes
830
-
831
- - b36de960: lruClearCache should just be clearCache for now
832
-
833
- ## 0.1.23
834
-
835
- ### Patch Changes
836
-
837
- - Bump packages to reflect new changest capabilities
838
- - Updated dependencies [undefined]
839
- - tina-graphql-helpers@0.1.1
840
-
841
- ## 0.1.22
842
-
843
- ### Patch Changes
844
-
845
- - Updated dependencies [undefined]
846
- - tina-graphql-helpers@0.1.0
847
-
848
- ## 0.1.21
849
-
850
- ### Patch Changes
851
-
852
- - Testin
853
-
854
- ## 0.1.20
855
-
856
- ### Patch Changes
857
-
858
- - Testing
859
-
860
- ## 0.1.13
861
-
862
- ### Patch Changes
863
-
864
- - Testing out changesets