@graphprotocol/hypergraph 0.1.0 → 0.3.0

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 (40) hide show
  1. package/dist/entity/findMany.js.map +1 -1
  2. package/dist/entity/types.d.ts +4 -4
  3. package/dist/entity/types.d.ts.map +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/mapping/Mapping.d.ts +418 -0
  9. package/dist/mapping/Mapping.d.ts.map +1 -0
  10. package/dist/mapping/Mapping.js +554 -0
  11. package/dist/mapping/Mapping.js.map +1 -0
  12. package/dist/mapping/Utils.d.ts +74 -0
  13. package/dist/mapping/Utils.d.ts.map +1 -0
  14. package/dist/mapping/Utils.js +144 -0
  15. package/dist/mapping/Utils.js.map +1 -0
  16. package/dist/mapping/index.d.ts +3 -0
  17. package/dist/mapping/index.d.ts.map +1 -0
  18. package/dist/mapping/index.js +3 -0
  19. package/dist/mapping/index.js.map +1 -0
  20. package/dist/store.d.ts +1 -1
  21. package/dist/store.d.ts.map +1 -1
  22. package/dist/store.js.map +1 -1
  23. package/dist/type/type.d.ts +3 -2
  24. package/dist/type/type.d.ts.map +1 -1
  25. package/dist/type/type.js +5 -2
  26. package/dist/type/type.js.map +1 -1
  27. package/dist/type-utils/type-utils.d.ts +7 -0
  28. package/dist/type-utils/type-utils.d.ts.map +1 -0
  29. package/dist/type-utils/type-utils.js +41 -0
  30. package/dist/type-utils/type-utils.js.map +1 -0
  31. package/package.json +3 -5
  32. package/src/entity/findMany.ts +4 -4
  33. package/src/entity/types.ts +4 -4
  34. package/src/index.ts +2 -0
  35. package/src/mapping/Mapping.ts +771 -0
  36. package/src/mapping/Utils.ts +156 -0
  37. package/src/mapping/index.ts +2 -0
  38. package/src/store.ts +1 -1
  39. package/src/type/type.ts +6 -2
  40. package/src/type-utils/type-utils.ts +46 -0
@@ -0,0 +1,156 @@
1
+ import { Data, String as EffectString } from 'effect';
2
+
3
+ /**
4
+ * Takes the input string and returns the camelCase equivalent
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { toCamelCase } from '@graphprotocol/hypergraph/mapping'
9
+ *
10
+ * expect(toCamelCase('Address line 1')).toEqual('addressLine1');
11
+ * expect(toCamelCase('AddressLine1')).toEqual('addressLine1');
12
+ * expect(toCamelCase('addressLine1')).toEqual('addressLine1');
13
+ * expect(toCamelCase('address_line_1')).toEqual('addressLine1');
14
+ * expect(toCamelCase('address-line-1')).toEqual('addressLine1');
15
+ * expect(toCamelCase('address-line_1')).toEqual('addressLine1');
16
+ * expect(toCamelCase('address-line 1')).toEqual('addressLine1');
17
+ * expect(toCamelCase('ADDRESS_LINE_1')).toEqual('addressLine1');
18
+ * ```
19
+ *
20
+ * @since 0.2.0
21
+ *
22
+ * @param str input string
23
+ * @returns camelCased value of the input string
24
+ */
25
+ export function toCamelCase(str: string): string {
26
+ if (EffectString.isEmpty(str)) {
27
+ throw new InvalidInputError({ input: str, cause: 'Input is empty' });
28
+ }
29
+
30
+ let result = '';
31
+ let capitalizeNext = false;
32
+ let i = 0;
33
+
34
+ // Skip leading non-alphanumeric characters
35
+ while (i < EffectString.length(str) && !/[a-zA-Z0-9]/.test(str[i])) {
36
+ i++;
37
+ }
38
+
39
+ for (; i < EffectString.length(str); i++) {
40
+ const char = str[i];
41
+
42
+ if (/[a-zA-Z0-9]/.test(char)) {
43
+ if (capitalizeNext) {
44
+ result += EffectString.toUpperCase(char);
45
+ capitalizeNext = false;
46
+ } else if (EffectString.length(result) === 0) {
47
+ // First character should always be lowercase
48
+ result += EffectString.toLowerCase(char);
49
+ } else if (/[A-Z]/.test(char) && i > 0 && /[a-z0-9]/.test(str[i - 1])) {
50
+ // Capital letter following lowercase/number - this indicates a word boundary
51
+ // So we need to capitalize this letter (it starts a new word)
52
+ result += EffectString.toUpperCase(char);
53
+ } else {
54
+ result += EffectString.toLowerCase(char);
55
+ }
56
+ } else {
57
+ // Non-alphanumeric character - set flag to capitalize next letter
58
+ capitalizeNext = EffectString.length(result) > 0; // Only capitalize if we have existing content
59
+ }
60
+ }
61
+
62
+ return result;
63
+ }
64
+
65
+ /**
66
+ * Takes the input string and returns the PascalCase equivalent
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * iimport { toPascalCase } from '@graphprotocol/hypergraph/mapping'
71
+ *
72
+ * expect(toPascalCase('Address line 1')).toEqual('AddressLine1');
73
+ * expect(toPascalCase('AddressLine1')).toEqual('AddressLine1');
74
+ * expect(toPascalCase('addressLine1')).toEqual('AddressLine1');
75
+ * expect(toPascalCase('address_line_1')).toEqual('AddressLine1');
76
+ * expect(toPascalCase('address-line-1')).toEqual('AddressLine1');
77
+ * expect(toPascalCase('address-line_1')).toEqual('AddressLine1');
78
+ * expect(toPascalCase('address-line 1')).toEqual('AddressLine1');
79
+ * expect(toPascalCase('ADDRESS_LINE_1')).toEqual('AddressLine1');
80
+ * ```
81
+ *
82
+ * @since 0.2.0
83
+ *
84
+ * @param str input string
85
+ * @returns PascalCased value of the input string
86
+ */
87
+ export function toPascalCase(str: string): string {
88
+ if (EffectString.isEmpty(str)) {
89
+ throw new InvalidInputError({ input: str, cause: 'Input is empty' });
90
+ }
91
+
92
+ let result = '';
93
+ let capitalizeNext = true; // Start with true to capitalize the first letter
94
+ let i = 0;
95
+
96
+ // Skip leading non-alphanumeric characters
97
+ while (i < EffectString.length(str) && !/[a-zA-Z0-9]/.test(str[i])) {
98
+ i++;
99
+ }
100
+
101
+ for (; i < EffectString.length(str); i++) {
102
+ const char = str[i];
103
+
104
+ if (/[a-zA-Z0-9]/.test(char)) {
105
+ if (capitalizeNext) {
106
+ result += EffectString.toUpperCase(char);
107
+ capitalizeNext = false;
108
+ } else if (/[A-Z]/.test(char) && i > 0 && /[a-z0-9]/.test(str[i - 1])) {
109
+ // Capital letter following lowercase/number - this indicates a word boundary
110
+ // So we need to capitalize this letter (it starts a new word)
111
+ result += EffectString.toUpperCase(char);
112
+ } else {
113
+ result += EffectString.toLowerCase(char);
114
+ }
115
+ } else {
116
+ // Non-alphanumeric character - set flag to capitalize next letter
117
+ capitalizeNext = true;
118
+ }
119
+ }
120
+
121
+ return result;
122
+ }
123
+
124
+ export class InvalidInputError extends Data.TaggedError('/typesync/errors/InvalidInputError')<{
125
+ readonly input: string;
126
+ readonly cause: unknown;
127
+ }> {}
128
+
129
+ /**
130
+ * Adds schema validation that the array of objects with property `name` only has unique names
131
+ *
132
+ * @example <caption>only unique names -> returns true</caption>
133
+ * ```ts
134
+ * const types = [{name:'Account'}, {name:'Event'}]
135
+ * expect(namesAreUnique(types)).toEqual(true)
136
+ * ```
137
+ *
138
+ * @example <caption>duplicate name -> returns false</caption>
139
+ * ```ts
140
+ * const types = [{name:'Account'}, {name:'Event'}, {name:'Account'}]
141
+ * expect(namesAreUnique(types)).toEqual(false)
142
+ * ```
143
+ */
144
+ export function namesAreUnique<T extends { readonly name: string }>(entries: ReadonlyArray<T>): boolean {
145
+ const names = new Set<string>();
146
+
147
+ for (const entry of entries) {
148
+ const name = EffectString.toLowerCase(entry.name);
149
+ if (names.has(name)) {
150
+ return false;
151
+ }
152
+ names.add(name);
153
+ }
154
+
155
+ return true;
156
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Mapping.js';
2
+ export * from './Utils.js';
package/src/store.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { AnyDocumentId, DocHandle, Repo } from '@automerge/automerge-repo';
2
- import type { Mapping } from '@graphprotocol/typesync/Mapping';
3
2
  import { createStore, type Store } from '@xstate/store';
4
3
  import type { PrivateAppIdentity } from './connect/types.js';
5
4
  import type { DocumentContent } from './entity/types.js';
6
5
  import { mergeMessages } from './inboxes/merge-messages.js';
7
6
  import type { InboxSenderAuthPolicy } from './inboxes/types.js';
7
+ import type { Mapping } from './mapping/Mapping.js';
8
8
  import type { Invitation, Updates } from './messages/index.js';
9
9
  import type { SpaceEvent, SpaceState } from './space-events/index.js';
10
10
  import { idToAutomergeId } from './utils/automergeId.js';
package/src/type/type.ts CHANGED
@@ -2,10 +2,12 @@ import * as Schema from 'effect/Schema';
2
2
  import { Field } from '../entity/entity.js';
3
3
  import type { AnyNoContext, EntityWithRelation } from '../entity/types.js';
4
4
 
5
- export const Text = Schema.String;
5
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
6
+ export const String = Schema.String;
6
7
  // biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
7
8
  export const Number = Schema.Number;
8
- export const Checkbox = Schema.Boolean;
9
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
10
+ export const Boolean = Schema.Boolean;
9
11
  // biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
10
12
  export const Date = Schema.Date;
11
13
  export const Point = Schema.transform(Schema.String, Schema.Array(Number), {
@@ -16,6 +18,8 @@ export const Point = Schema.transform(Schema.String, Schema.Array(Number), {
16
18
  encode: (points: readonly number[]) => points.join(','),
17
19
  });
18
20
 
21
+ export const optional = Schema.optional;
22
+
19
23
  export const Relation = <S extends AnyNoContext>(schema: S) => {
20
24
  const relationSchema = Field({
21
25
  select: Schema.Array(schema) as unknown as Schema.Schema<ReadonlyArray<EntityWithRelation<S>>>,
@@ -0,0 +1,46 @@
1
+ import * as Type from '../type/type.js';
2
+
3
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
4
+ export const isStringOrOptionalStringType = (type: any) => {
5
+ if (type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional) {
6
+ return type.from === Type.String;
7
+ }
8
+ return type === Type.String;
9
+ };
10
+
11
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
12
+ export const isNumberOrOptionalNumberType = (type: any) => {
13
+ if (type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional) {
14
+ return type.from === Type.Number;
15
+ }
16
+ return type === Type.Number;
17
+ };
18
+
19
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
20
+ export const isDateOrOptionalDateType = (type: any) => {
21
+ if (type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional) {
22
+ return type.from === Type.Date;
23
+ }
24
+ return type === Type.Date;
25
+ };
26
+
27
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
28
+ export const isBooleanOrOptionalBooleanType = (type: any) => {
29
+ if (type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional) {
30
+ return type.from === Type.Boolean;
31
+ }
32
+ return type === Type.Boolean;
33
+ };
34
+
35
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
36
+ export const isPointOrOptionalPointType = (type: any) => {
37
+ if (type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional) {
38
+ return type.from === Type.Point;
39
+ }
40
+ return type === Type.Point;
41
+ };
42
+
43
+ // biome-ignore lint/suspicious/noExplicitAny: TODO
44
+ export const isOptional = (type: any) => {
45
+ return type.ast && type.ast._tag === 'PropertySignatureDeclaration' && type.ast.isOptional;
46
+ };