@shaxpir/duiduidui-models 1.0.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 (211) hide show
  1. package/README.md +1 -0
  2. package/decs.d.ts +87 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +20 -0
  5. package/dist/models/OutboundMessage.d.ts +18 -0
  6. package/dist/models/OutboundMessage.js +25 -0
  7. package/dist/models/content/Activity.d.ts +10 -0
  8. package/dist/models/content/Activity.js +2 -0
  9. package/dist/models/content/ArrayView.d.ts +26 -0
  10. package/dist/models/content/ArrayView.js +174 -0
  11. package/dist/models/content/Billing.d.ts +144 -0
  12. package/dist/models/content/Billing.js +418 -0
  13. package/dist/models/content/Book.d.ts +77 -0
  14. package/dist/models/content/Book.js +407 -0
  15. package/dist/models/content/Category.d.ts +16 -0
  16. package/dist/models/content/Category.js +20 -0
  17. package/dist/models/content/Checkpointable.d.ts +21 -0
  18. package/dist/models/content/Checkpointable.js +156 -0
  19. package/dist/models/content/Comment.d.ts +19 -0
  20. package/dist/models/content/Comment.js +53 -0
  21. package/dist/models/content/ConceptArt.d.ts +31 -0
  22. package/dist/models/content/ConceptArt.js +84 -0
  23. package/dist/models/content/Content.d.ts +52 -0
  24. package/dist/models/content/Content.js +61 -0
  25. package/dist/models/content/ContentKind.d.ts +10 -0
  26. package/dist/models/content/ContentKind.js +16 -0
  27. package/dist/models/content/Context.d.ts +28 -0
  28. package/dist/models/content/Context.js +162 -0
  29. package/dist/models/content/DevEnv.d.ts +5 -0
  30. package/dist/models/content/DevEnv.js +9 -0
  31. package/dist/models/content/Device.d.ts +24 -0
  32. package/dist/models/content/Device.js +62 -0
  33. package/dist/models/content/Dictionary.d.ts +31 -0
  34. package/dist/models/content/Dictionary.js +5 -0
  35. package/dist/models/content/DictionaryEntry.d.ts +20 -0
  36. package/dist/models/content/DictionaryEntry.js +2 -0
  37. package/dist/models/content/ElasticModel.d.ts +149 -0
  38. package/dist/models/content/ElasticModel.js +179 -0
  39. package/dist/models/content/Environment.d.ts +61 -0
  40. package/dist/models/content/Environment.js +124 -0
  41. package/dist/models/content/ExportOptions.d.ts +64 -0
  42. package/dist/models/content/ExportOptions.js +213 -0
  43. package/dist/models/content/Folder.d.ts +16 -0
  44. package/dist/models/content/Folder.js +33 -0
  45. package/dist/models/content/Fragment.d.ts +54 -0
  46. package/dist/models/content/Fragment.js +181 -0
  47. package/dist/models/content/GeoLocation.d.ts +4 -0
  48. package/dist/models/content/GeoLocation.js +2 -0
  49. package/dist/models/content/Hanzi.d.ts +21 -0
  50. package/dist/models/content/Hanzi.js +2 -0
  51. package/dist/models/content/HighlightRule.d.ts +9 -0
  52. package/dist/models/content/HighlightRule.js +2 -0
  53. package/dist/models/content/Manifest.d.ts +42 -0
  54. package/dist/models/content/Manifest.js +114 -0
  55. package/dist/models/content/Media.d.ts +32 -0
  56. package/dist/models/content/Media.js +98 -0
  57. package/dist/models/content/Metric.d.ts +46 -0
  58. package/dist/models/content/Metric.js +183 -0
  59. package/dist/models/content/Migration.d.ts +68 -0
  60. package/dist/models/content/Migration.js +155 -0
  61. package/dist/models/content/Model.d.ts +45 -0
  62. package/dist/models/content/Model.js +280 -0
  63. package/dist/models/content/Permissions.d.ts +7 -0
  64. package/dist/models/content/Permissions.js +20 -0
  65. package/dist/models/content/Phrase.d.ts +8 -0
  66. package/dist/models/content/Phrase.js +2 -0
  67. package/dist/models/content/Placeholder.d.ts +8 -0
  68. package/dist/models/content/Placeholder.js +36 -0
  69. package/dist/models/content/Profile.d.ts +30 -0
  70. package/dist/models/content/Profile.js +95 -0
  71. package/dist/models/content/RichText.d.ts +58 -0
  72. package/dist/models/content/RichText.js +79 -0
  73. package/dist/models/content/Session.d.ts +39 -0
  74. package/dist/models/content/Session.js +173 -0
  75. package/dist/models/content/Speech.d.ts +67 -0
  76. package/dist/models/content/Speech.js +97 -0
  77. package/dist/models/content/Stub.d.ts +24 -0
  78. package/dist/models/content/Stub.js +179 -0
  79. package/dist/models/content/Time.d.ts +56 -0
  80. package/dist/models/content/Time.js +295 -0
  81. package/dist/models/content/User.d.ts +36 -0
  82. package/dist/models/content/User.js +95 -0
  83. package/dist/models/content/Workspace.d.ts +71 -0
  84. package/dist/models/content/Workspace.js +237 -0
  85. package/dist/models/content/index.d.ts +36 -0
  86. package/dist/models/content/index.js +53 -0
  87. package/dist/models/index.d.ts +4 -0
  88. package/dist/models/index.js +21 -0
  89. package/dist/models/legacy/LegacyBodyFormat.d.ts +9 -0
  90. package/dist/models/legacy/LegacyBodyFormat.js +2 -0
  91. package/dist/models/legacy/LegacyComment.d.ts +12 -0
  92. package/dist/models/legacy/LegacyComment.js +2 -0
  93. package/dist/models/legacy/LegacyContent.d.ts +53 -0
  94. package/dist/models/legacy/LegacyContent.js +55 -0
  95. package/dist/models/legacy/LegacyConversion.d.ts +55 -0
  96. package/dist/models/legacy/LegacyConversion.js +401 -0
  97. package/dist/models/legacy/LegacyFragment.d.ts +21 -0
  98. package/dist/models/legacy/LegacyFragment.js +2 -0
  99. package/dist/models/legacy/LegacyLocator.d.ts +8 -0
  100. package/dist/models/legacy/LegacyLocator.js +31 -0
  101. package/dist/models/legacy/LegacyOutboundMessage.d.ts +16 -0
  102. package/dist/models/legacy/LegacyOutboundMessage.js +13 -0
  103. package/dist/models/legacy/LegacyPicture.d.ts +14 -0
  104. package/dist/models/legacy/LegacyPicture.js +2 -0
  105. package/dist/models/legacy/LegacyProfile.d.ts +9 -0
  106. package/dist/models/legacy/LegacyProfile.js +2 -0
  107. package/dist/models/legacy/LegacySession.d.ts +41 -0
  108. package/dist/models/legacy/LegacySession.js +35 -0
  109. package/dist/models/legacy/LegacyStory.d.ts +23 -0
  110. package/dist/models/legacy/LegacyStory.js +2 -0
  111. package/dist/models/legacy/LegacyStub.d.ts +15 -0
  112. package/dist/models/legacy/LegacyStub.js +2 -0
  113. package/dist/models/legacy/LegacyTransaction.d.ts +14 -0
  114. package/dist/models/legacy/LegacyTransaction.js +49 -0
  115. package/dist/models/legacy/LegacyUser.d.ts +28 -0
  116. package/dist/models/legacy/LegacyUser.js +32 -0
  117. package/dist/models/legacy/LegacyWorkspace.d.ts +23 -0
  118. package/dist/models/legacy/LegacyWorkspace.js +6 -0
  119. package/dist/models/legacy/index.d.ts +15 -0
  120. package/dist/models/legacy/index.js +32 -0
  121. package/dist/models/markup/BodyFormat.d.ts +14 -0
  122. package/dist/models/markup/BodyFormat.js +190 -0
  123. package/dist/models/markup/ChangeModel.d.ts +22 -0
  124. package/dist/models/markup/ChangeModel.js +107 -0
  125. package/dist/models/markup/DeltaOps.d.ts +5 -0
  126. package/dist/models/markup/DeltaOps.js +74 -0
  127. package/dist/models/markup/HtmlMarkup.d.ts +4 -0
  128. package/dist/models/markup/HtmlMarkup.js +21 -0
  129. package/dist/models/markup/Operation.d.ts +32 -0
  130. package/dist/models/markup/Operation.js +194 -0
  131. package/dist/models/markup/TextEditOps.d.ts +9 -0
  132. package/dist/models/markup/TextEditOps.js +50 -0
  133. package/dist/models/markup/index.d.ts +6 -0
  134. package/dist/models/markup/index.js +23 -0
  135. package/dist/repo/ConnectionListener.d.ts +9 -0
  136. package/dist/repo/ConnectionListener.js +21 -0
  137. package/dist/repo/PermissiveJson1.d.ts +58 -0
  138. package/dist/repo/PermissiveJson1.js +39 -0
  139. package/dist/repo/ShareSync.d.ts +60 -0
  140. package/dist/repo/ShareSync.js +348 -0
  141. package/dist/repo/index.d.ts +3 -0
  142. package/dist/repo/index.js +20 -0
  143. package/dist/util/Async.d.ts +8 -0
  144. package/dist/util/Async.js +18 -0
  145. package/dist/util/Base62.d.ts +6 -0
  146. package/dist/util/Base62.js +47 -0
  147. package/dist/util/BinarySearch.d.ts +7 -0
  148. package/dist/util/BinarySearch.js +46 -0
  149. package/dist/util/CachingHasher.d.ts +8 -0
  150. package/dist/util/CachingHasher.js +41 -0
  151. package/dist/util/Color.d.ts +32 -0
  152. package/dist/util/Color.js +204 -0
  153. package/dist/util/Dispatch.d.ts +15 -0
  154. package/dist/util/Dispatch.js +79 -0
  155. package/dist/util/EditDistance.d.ts +13 -0
  156. package/dist/util/EditDistance.js +184 -0
  157. package/dist/util/Encryption.d.ts +5 -0
  158. package/dist/util/Encryption.js +2 -0
  159. package/dist/util/Logging.d.ts +108 -0
  160. package/dist/util/Logging.js +412 -0
  161. package/dist/util/NumberFormat.d.ts +14 -0
  162. package/dist/util/NumberFormat.js +224 -0
  163. package/dist/util/Struct.d.ts +4 -0
  164. package/dist/util/Struct.js +15 -0
  165. package/dist/util/Template.d.ts +16 -0
  166. package/dist/util/Template.js +128 -0
  167. package/dist/util/Text.d.ts +45 -0
  168. package/dist/util/Text.js +243 -0
  169. package/dist/util/Tuples.d.ts +9 -0
  170. package/dist/util/Tuples.js +135 -0
  171. package/dist/util/Validate.d.ts +4 -0
  172. package/dist/util/Validate.js +11 -0
  173. package/dist/util/Vocabulary.d.ts +3 -0
  174. package/dist/util/Vocabulary.js +35 -0
  175. package/dist/util/index.d.ts +16 -0
  176. package/dist/util/index.js +33 -0
  177. package/lib/models/content/ArrayView.ts +203 -0
  178. package/lib/models/content/Billing.ts +558 -0
  179. package/lib/models/content/Content.ts +110 -0
  180. package/lib/models/content/ContentKind.ts +14 -0
  181. package/lib/models/content/DevEnv.ts +5 -0
  182. package/lib/models/content/Device.ts +86 -0
  183. package/lib/models/content/DictionaryEntry.ts +22 -0
  184. package/lib/models/content/GeoLocation.ts +4 -0
  185. package/lib/models/content/Hanzi.ts +25 -0
  186. package/lib/models/content/Manifest.ts +162 -0
  187. package/lib/models/content/Media.ts +126 -0
  188. package/lib/models/content/Model.ts +327 -0
  189. package/lib/models/content/Permissions.ts +21 -0
  190. package/lib/models/content/Phrase.ts +10 -0
  191. package/lib/models/content/Profile.ts +119 -0
  192. package/lib/models/content/Time.ts +328 -0
  193. package/lib/models/content/User.ts +130 -0
  194. package/lib/models/markup/ChangeModel.ts +95 -0
  195. package/lib/models/markup/DeltaOps.ts +71 -0
  196. package/lib/models/markup/Operation.ts +215 -0
  197. package/lib/models/markup/TextEditOps.ts +50 -0
  198. package/lib/repo/ConnectionListener.ts +25 -0
  199. package/lib/repo/PermissiveJson1.ts +14 -0
  200. package/lib/repo/ShareSync.ts +390 -0
  201. package/lib/util/Base62.ts +47 -0
  202. package/lib/util/CachingHasher.ts +38 -0
  203. package/lib/util/Dispatch.ts +92 -0
  204. package/lib/util/Encryption.ts +5 -0
  205. package/lib/util/Logging.ts +568 -0
  206. package/lib/util/NumberFormat.ts +194 -0
  207. package/lib/util/Struct.ts +14 -0
  208. package/lib/util/Tuples.ts +131 -0
  209. package/package.json +41 -0
  210. package/tsconfig.json +25 -0
  211. package/tslint.json +46 -0
@@ -0,0 +1,328 @@
1
+ import dayjs from 'dayjs';
2
+ import utc from 'dayjs/plugin/utc';
3
+ import timezone from 'dayjs/plugin/timezone';
4
+ import dayOfYear from 'dayjs/plugin/dayOfYear';
5
+ dayjs.extend(utc);
6
+ dayjs.extend(timezone);
7
+ dayjs.extend(dayOfYear);
8
+
9
+ enum CompactDateTimeBrand {}
10
+ enum CompactDateBrand {}
11
+
12
+ export type CompactDateTime = string & CompactDateTimeBrand;
13
+ export type CompactDate = string & CompactDateBrand;
14
+
15
+ export interface SingleTime {
16
+ utc_time:CompactDateTime;
17
+ }
18
+
19
+ export interface MultiTime {
20
+ utc_time:CompactDateTime;
21
+ local_time:CompactDateTime;
22
+ }
23
+
24
+ export class MultiClock {
25
+
26
+ public static utcNow():MultiTime {
27
+ const localtime:CompactDateTime = Time.local();
28
+ const utcTime:CompactDateTime = Time.utc(localtime);
29
+ return {
30
+ local_time: utcTime,
31
+ utc_time: utcTime
32
+ };
33
+ }
34
+
35
+ public static now():MultiTime {
36
+ const localtime:CompactDateTime = Time.local();
37
+ return {
38
+ local_time: localtime,
39
+ utc_time: Time.utc(localtime)
40
+ };
41
+ }
42
+ }
43
+
44
+ export class Time {
45
+
46
+ public static readonly DATETIME_FORMAT_LEGACY:string = "YYYY-MM-DDTHH:mm:ss.SSSZ";
47
+
48
+ public static readonly DATE_FORMAT:string = "YYYYMMDD";
49
+ public static readonly DATETIME_FORMAT_COMPACT:string = "YYYYMMDDHHmmssSSS";
50
+
51
+ public static readonly DATE_NO_YEAR:string = "MMMM D";
52
+ public static readonly DATE_BEAUTIFIED:string = "MMMM D, YYYY";
53
+ public static readonly DATE_FULLY_BEAUTIFIED:string = "dddd, MMMM D, YYYY";
54
+
55
+ public static readonly YEAR_ONLY:string = "YYYY";
56
+
57
+ public static asCompactDateTime(text:string):CompactDateTime {
58
+ // A compact datetime is always 17 digits long and starts with '20'
59
+ if (text.startsWith("20") && /^\d{17}$/.test(text)) {
60
+ return text as CompactDateTime;
61
+ }
62
+ return null as CompactDateTime;
63
+ }
64
+
65
+ public static asCompactDate(text:string):CompactDate|null {
66
+ // A compact date is always 8 digits long and starts with '20'
67
+ if (text.startsWith("20") && /^\d{8}$/.test(text)) {
68
+ return text as CompactDate;
69
+ }
70
+ return null;
71
+ }
72
+
73
+ public static isDateBefore(a:CompactDate, b:CompactDate):boolean {
74
+ return Time.compareDate(a, b) < 0;
75
+ }
76
+
77
+ public static isDateAfter(a:CompactDate, b:CompactDate):boolean {
78
+ return Time.compareDate(a, b) > 0;
79
+ }
80
+
81
+ public static compareDate(a:CompactDate, b:CompactDate):number {
82
+ if (a == null && b == null) {
83
+ return 0;
84
+ } else if (a == null) {
85
+ return -1;
86
+ } else if (b == null) {
87
+ return 1;
88
+ }
89
+ return (a as string).localeCompare(b as string);
90
+ }
91
+
92
+ public static isDateTimeBefore(a:CompactDateTime, b:CompactDateTime):boolean {
93
+ return Time.compareDateTime(a, b) < 0;
94
+ }
95
+
96
+ public static isDateTimeAfter(a:CompactDateTime, b:CompactDateTime):boolean {
97
+ return Time.compareDateTime(a, b) > 0;
98
+ }
99
+
100
+ public static compareDateTime(a:CompactDateTime, b:CompactDateTime):number {
101
+ if (a == null && b == null) {
102
+ return 0;
103
+ } else if (a == null) {
104
+ return -1;
105
+ } else if (b == null) {
106
+ return 1;
107
+ }
108
+ return (a as string).localeCompare(b as string);
109
+ }
110
+
111
+ public static sortByUtcTime(array:MultiTime[]):MultiTime[] {
112
+ return array.sort((a, b) => Time.compareDateTime(a.utc_time, b.utc_time));
113
+ }
114
+
115
+ public static dateFrom(timestamp:CompactDateTime):CompactDate {
116
+ const timestampText = timestamp as string;
117
+ if (timestampText.length >= 8 && /^\d{8}.*/.test(timestampText)) {
118
+ return timestampText.substring(0, 8) as CompactDate;
119
+ }
120
+ if (typeof(timestamp) === "string") {
121
+ throw new Error("can\x27t parse date from malformed timestamp: " + timestamp);
122
+ } else {
123
+ throw new Error("can\x27t parse date from malformed timestamp: " + JSON.stringify(timestamp));
124
+ }
125
+ }
126
+
127
+ public static datesBetween(minDate:CompactDate, maxDate:CompactDate):CompactDate[] {
128
+ const dates:CompactDate[] = new Array<CompactDate>();
129
+ let d:string = minDate;
130
+ while (d.localeCompare(minDate) >= 0 && d.localeCompare(maxDate) <= 0) {
131
+ dates.push(d as CompactDate);
132
+ d = dayjs(d, Time.DATE_FORMAT).add(1, 'days').format(Time.DATE_FORMAT) as CompactDate;
133
+ }
134
+ return dates;
135
+ }
136
+
137
+ public static plus(timestamp:CompactDateTime, amount:number, units:dayjs.ManipulateType):CompactDateTime {
138
+ const time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
139
+ return time.add(amount, units).format(Time.DATETIME_FORMAT_COMPACT) as CompactDateTime;
140
+ }
141
+
142
+ public static todayLocal():CompactDate {
143
+ return Time.dateFrom(Time.local());
144
+ }
145
+
146
+ public static todayUtc():CompactDate {
147
+ return Time.dateFrom(Time.utc());
148
+ }
149
+
150
+ public static startOfMonth(date:CompactDate):CompactDate {
151
+ let text = date as string;
152
+ if (text.startsWith("20") && /^\d{8}$/.test(text)) {
153
+ text = text.substring(0, 6) + "01;"
154
+ }
155
+ return text as CompactDate;
156
+ }
157
+
158
+ public static local(utc?:CompactDateTime):CompactDateTime {
159
+ let result;
160
+ if (typeof(utc) === "undefined") {
161
+ result = dayjs().format(Time.DATETIME_FORMAT_COMPACT);
162
+ } else {
163
+ const timestamp = dayjs.utc(utc, Time.DATETIME_FORMAT_COMPACT)
164
+ .local()
165
+ ;
166
+ result = timestamp.format(Time.DATETIME_FORMAT_COMPACT);
167
+ }
168
+ return result as CompactDateTime;
169
+ }
170
+
171
+ // TODO: Should this actually be called localFromEpochSeconds? Passing zero
172
+ // returns timestamp that is local-timezone hours offset from UTC epoch time.
173
+ public static utcFromEpochSeconds(seconds:number):CompactDateTime {
174
+ return dayjs.unix(seconds).utc().format(Time.DATETIME_FORMAT_COMPACT) as CompactDateTime;
175
+ }
176
+
177
+ public static utc(local?:CompactDateTime):CompactDateTime {
178
+ let result;
179
+ if (typeof(local) === "undefined") {
180
+ result = dayjs.utc().format(Time.DATETIME_FORMAT_COMPACT);
181
+ } else {
182
+ const timestamp = dayjs(local, Time.DATETIME_FORMAT_COMPACT)
183
+ .utc()
184
+ ;
185
+ result = timestamp.format(Time.DATETIME_FORMAT_COMPACT);
186
+ }
187
+ return result as CompactDateTime;
188
+ }
189
+
190
+ public static format(timestamp:CompactDateTime, pattern:string, isLocal:boolean):CompactDateTime {
191
+ let time = null;
192
+ if (isLocal) {
193
+ time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
194
+ } else {
195
+ time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
196
+ .local()
197
+ ;
198
+ }
199
+ return time.format(pattern) as CompactDateTime;
200
+ }
201
+
202
+ public static formatFromDate(date:Date, pattern:string):string {
203
+ const time = dayjs(date);
204
+ return time.format(pattern);
205
+ }
206
+
207
+ public static beautify(
208
+ timestamp:CompactDateTime,
209
+ isLocal:boolean = false,
210
+ amount?:number,
211
+ units?:string
212
+ ):string {
213
+ const now:any = dayjs();
214
+ let time:any = null;
215
+ if (isLocal) {
216
+ time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
217
+ } else {
218
+ time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
219
+ .local()
220
+ ;
221
+ }
222
+
223
+ // If the 'num' and 'units' params exist, then use them to manipulate the timestamp
224
+ if (typeof(amount) === "number" && typeof(units) === "string") {
225
+ time = time.add(amount, units);
226
+ }
227
+
228
+ let datePart:string = "", timePart:string = "";
229
+
230
+ const timeDayOfYear:number = time.dayOfYear();
231
+ const nowDayOfYear:number = now.dayOfYear();
232
+ const timeYear:number = time.year();
233
+ const nowYear:number = now.year();
234
+ if (nowDayOfYear === timeDayOfYear && nowYear === timeYear) {
235
+ datePart = "Today";
236
+ } else if (nowDayOfYear - timeDayOfYear === 1 && nowYear === timeYear) {
237
+ datePart = "Yesterday";
238
+ } else if (nowDayOfYear === 1 && timeDayOfYear === 365 && timeYear % 4 !== 0 && nowYear - timeYear === 1) {
239
+ datePart = "Yesterday";
240
+ } else if (nowDayOfYear === 1 && timeDayOfYear === 366 && timeYear % 4 === 0 && nowYear - timeYear === 1) {
241
+ datePart = "Yesterday";
242
+
243
+ } else if (timeDayOfYear - nowDayOfYear === 1 && timeYear === nowYear) {
244
+ datePart = "Tomorrow";
245
+ } else if (timeDayOfYear === 1 && nowDayOfYear === 365 && nowYear % 4 !== 0 && timeYear - nowYear === 1) {
246
+ datePart = "Tomorrow";
247
+ } else if (timeDayOfYear === 1 && nowDayOfYear === 366 && nowYear % 4 === 0 && timeYear - nowYear === 1) {
248
+ datePart = "Tomorrow";
249
+
250
+ } else if (Math.abs(now.diff(time, "days")) <= 30) {
251
+ datePart = time.format(Time.DATE_FULLY_BEAUTIFIED);
252
+ } else {
253
+ datePart = time.format(Time.DATE_BEAUTIFIED);
254
+ }
255
+
256
+ if (Math.abs(now.diff(time, "days")) <= 7) {
257
+ timePart = time.format("h:mm a");
258
+ timePart = timePart.replace(" am", "a");
259
+ timePart = timePart.replace(" pm", "p");
260
+ }
261
+
262
+ if (timePart.length === 0) {
263
+ return datePart;
264
+ } else {
265
+ return datePart + ", " + timePart;
266
+ }
267
+ }
268
+
269
+ public static absDiffMinutes(a:CompactDateTime, b:CompactDateTime):number {
270
+ const aMoment = dayjs(a, Time.DATETIME_FORMAT_COMPACT);
271
+ const bMoment = dayjs(b, Time.DATETIME_FORMAT_COMPACT);
272
+ return Math.abs(aMoment.diff(bMoment, 'minutes', true));
273
+ }
274
+
275
+ public static elapsed(timestamp:CompactDateTime, isLocal:boolean):string {
276
+ let time = null;
277
+ if (isLocal) {
278
+ time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
279
+ } else {
280
+ time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
281
+ .local()
282
+ ;
283
+ }
284
+ const difference = dayjs().diff(time, 'seconds');
285
+ if (difference < 60) {
286
+ return difference === 1 ? "1 second ago" : difference + " seconds ago";
287
+ } else if (difference < 3 * 60) {
288
+ const minutes = Math.floor(difference / 60);
289
+ const seconds = Math.floor(difference % 60);
290
+ const minText = minutes === 1 ? "1 minute" : minutes + " minutes";
291
+ const secText = seconds === 1 ? "1 second" : seconds + " seconds";
292
+ return minText + ", " + secText + " ago";
293
+ } else if (difference < 60 * 60) {
294
+ const minutes = Math.round(difference / 60);
295
+ return "about " + minutes + " minutes ago";
296
+ } else if (difference < 24 * 60 * 60) {
297
+ const hours = Math.round(difference / (60 * 60));
298
+ return "about " + hours + " hours ago";
299
+ } else {
300
+ return Time.beautify(timestamp, isLocal);
301
+ }
302
+ }
303
+
304
+ public static timeUntil(utcTime:CompactDateTime, units:dayjs.UnitType):number {
305
+ return - Time.timeSince(utcTime, units);
306
+ }
307
+
308
+ public static timeSince(utcTime:CompactDateTime, units:dayjs.UnitType):number {
309
+ const then = dayjs.utc(utcTime, Time.DATETIME_FORMAT_COMPACT);
310
+ const now = dayjs().utc();
311
+ return now.diff(then, units, true);
312
+ }
313
+
314
+ public static currentYear():string {
315
+ return dayjs().format(Time.YEAR_ONLY);
316
+ }
317
+
318
+ public static humanizedDatePacific(epochSeconds:number):string {
319
+ return dayjs.unix(epochSeconds).tz("America/Los_Angeles").format("MMMM D, YYYY");
320
+ }
321
+
322
+ // TODO: Should this actually expect seconds instead of milliseconds?
323
+ public static humanizedTimePacific(epochSeconds:number):string {
324
+ return dayjs(epochSeconds).tz("America/Los_Angeles").format("h:mm:ss a") + " (Pacific)";
325
+ }
326
+
327
+
328
+ }
@@ -0,0 +1,130 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { ShareSync, ShareSyncFactory } from '../../repo/ShareSync';
3
+ import { CachingHasher } from '../../util/CachingHasher';
4
+ import { BatchOperation } from '../markup/Operation';
5
+ import { Content, ContentId, ContentMeta } from "./Content";
6
+ import { ContentKind } from './ContentKind';
7
+ import { MultiClock, MultiTime } from "./Time";
8
+
9
+ export interface UserPayload {
10
+ email:string;
11
+ phone:string;
12
+ force_password_reset:boolean;
13
+ pass_md5_salt_md5:string;
14
+ salt:string;
15
+ }
16
+
17
+ export interface ModernUserBody {
18
+ meta:ContentMeta;
19
+ payload:UserPayload;
20
+ }
21
+
22
+ export class ModernUser extends Content {
23
+
24
+ public static readonly MIN_PASSWORD_LENGTH:number = 8;
25
+
26
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
27
+ super(doc, shouldAcquire, shareSync);
28
+ }
29
+
30
+ public get payload():UserPayload {
31
+ return this.doc.data.payload as UserPayload;
32
+ }
33
+
34
+ public static create(
35
+ userId:ContentId,
36
+ payload:UserPayload,
37
+ createdAt?:MultiTime
38
+ ):ModernUser {
39
+ // User creation always happens in UTC time
40
+ const now = MultiClock.utcNow();
41
+ // If the createdAt time is not provided, use the current time
42
+ createdAt ??= now;
43
+ return ShareSyncFactory.get().createContent(
44
+ {
45
+ meta : {
46
+ ref : userId,
47
+ is_head : true,
48
+ kind : ContentKind.USER,
49
+ id : userId,
50
+ owner : userId,
51
+ created_at : createdAt,
52
+ updated_at : now
53
+ },
54
+ payload : payload
55
+ }
56
+ ) as ModernUser;
57
+ }
58
+
59
+ public get email():string {
60
+ return this.payload.email;
61
+ }
62
+ public setEmail(value:string) {
63
+ if (this.email != value) {
64
+ const batch = new BatchOperation(this);
65
+ batch.setPathValue([ 'payload', 'email' ] , value);
66
+ batch.commit();
67
+ }
68
+ }
69
+
70
+ public get forcePasswordReset():boolean {
71
+ return this.payload.force_password_reset;
72
+ }
73
+ public setForcePasswordReset(value:boolean) {
74
+ if (this.forcePasswordReset != value) {
75
+ const batch = new BatchOperation(this);
76
+ batch.setPathValue([ 'payload', 'force_password_reset' ] , value);
77
+ batch.commit();
78
+ }
79
+ }
80
+
81
+ public get passMd5SaltMd5():string {
82
+ return this.payload.pass_md5_salt_md5;
83
+ }
84
+ public setPassMd5SaltMd5(value:string) {
85
+ if (this.passMd5SaltMd5 != value) {
86
+ const batch = new BatchOperation(this);
87
+ batch.setPathValue([ 'payload', 'pass_md5_salt_md5' ] , value);
88
+ batch.commit();
89
+ }
90
+ }
91
+
92
+ public get salt():string {
93
+ return this.payload.salt;
94
+ }
95
+
96
+ public hasValidPassword(password:string):boolean {
97
+ const passMd5 = ModernUser.makePassMd5(password);
98
+ return this.hasValidPassMd5(passMd5);
99
+ }
100
+
101
+ public hasValidPassMd5(passMd5:string):boolean {
102
+ return ModernUser.isValidPassMd5(passMd5, this.payload);
103
+ }
104
+
105
+ public static makePassMd5SaltMd5(salt:string, password:string):string {
106
+ const passMd5 = ModernUser.makePassMd5(password);
107
+ return ModernUser.addSaltToPassMd5(salt, passMd5);
108
+ }
109
+
110
+ public static addSaltToPassMd5(salt:string, passMd5:string):string {
111
+ return CachingHasher.makeMd5Hash(passMd5 + salt);
112
+ }
113
+
114
+ public static makePassMd5(password:string):string {
115
+ return CachingHasher.makeMd5Hash(password);
116
+ }
117
+
118
+ public static isValidPassMd5(passMd5:string, userPayload:UserPayload):boolean {
119
+ const passMd5SaltMd5 = ModernUser.addSaltToPassMd5(userPayload.salt, passMd5);
120
+ return passMd5SaltMd5 === userPayload.pass_md5_salt_md5;
121
+ }
122
+
123
+ public static async findByEmail(email:string):Promise<ModernUser[]> {
124
+ const shareSync = ShareSyncFactory.get();
125
+ return shareSync.findAndAcquire(
126
+ ContentKind.USER, { "payload.email" : email }
127
+ ) as Promise<ModernUser[]>;
128
+ }
129
+
130
+ }
@@ -0,0 +1,95 @@
1
+ import { Op as DeltaOp } from "@shaxpir/quill-delta";
2
+ import * as Changesets from 'json-diff-ts';
3
+ import { Operation } from "json-diff-ts";
4
+ import { Tuples } from "../../util/Tuples";
5
+ import { Model } from "../content/Model";
6
+
7
+ export interface ChangeItem {
8
+ type:Operation;
9
+ path:string;
10
+ value?:any;
11
+ oldValue?:any;
12
+ }
13
+
14
+ export interface ModelChange {
15
+ local:boolean;
16
+ model:Model;
17
+ items:ChangeItem[];
18
+ op:DeltaOp[];
19
+ }
20
+
21
+ export class Changes {
22
+ public static withPathPrefix(change:ModelChange, prefix:string):boolean {
23
+ let matchingItems:ChangeItem[] = Changes.havingPathPrefix(change, prefix);
24
+ return matchingItems.length > 0;
25
+ }
26
+ public static havingPathPrefix(change:ModelChange, prefix:string):ChangeItem[] {
27
+ let matchingItems:ChangeItem[] = [];
28
+ const items = change.items;
29
+ for (let i = 0, len = items.length; i < len; i++) {
30
+ const item = items[i];
31
+ if (item.path.startsWith(prefix)) {
32
+ matchingItems.push(item);
33
+ }
34
+ }
35
+ return matchingItems;
36
+ }
37
+ }
38
+
39
+ // We have our own ChangeModel logic for performaning a diff between two objects. The main difference
40
+ // between our model and the json-diff implementation is that we care about enumerating the full-path
41
+ // to all modified model data elements, so that various GUI mediators can match against those paths
42
+ // and decide whether or not to re-render a view. So, when a deeply-nested object is added into
43
+ // another deeply nested object, the change-model should include the full path to all new R-Values
44
+ // added in the change, rather than just the path to the root of the new tree branches.
45
+ export class ChangeModel {
46
+ public static between(before:any, after:any):ChangeItem[] {
47
+ let changeItems:ChangeItem[] = [];
48
+ const diff = Changesets.flattenChangeset(
49
+ Changesets.diff(before, after)
50
+ );
51
+ for (let i = 0; i < diff.length; i++) {
52
+ let diffItem = diff[i];
53
+ let type:Operation = diffItem.type;
54
+ let path:string = diffItem.path;
55
+ let value:any = diffItem.value;
56
+ let oldValue:any = diffItem.oldValue;
57
+ let valueType:string = diffItem.valueType;
58
+ let key:string = diffItem.key;
59
+ if (valueType === 'Object' || valueType == 'Array') {
60
+ let tuples = Tuples.of(value);
61
+ let fullPath = path;
62
+ if (/^\d+$/.test(key) && !path.endsWith('[' + key + ']')) {
63
+ fullPath = path + '[' + key + ']';
64
+ }
65
+ if (!/^\d+$/.test(key) && !path.endsWith('.' + key)) {
66
+ fullPath = path + '.' + key;
67
+ }
68
+ if (tuples.length == 0) {
69
+ if (type == 'REMOVE') {
70
+ changeItems.push({ type : type, path : fullPath, oldValue : value });
71
+ } else {
72
+ changeItems.push({ type : type, path : fullPath, value : value, oldValue : oldValue });
73
+ }
74
+ } else {
75
+ for (let j = 0; j < tuples.length; j++) {
76
+ let tuple = tuples[j];
77
+ let fullTuplePath = Tuples.stringifyPath(fullPath, tuple.path);
78
+ if (type == 'REMOVE') {
79
+ changeItems.push({ type : type, path : fullTuplePath, oldValue : tuple.val });
80
+ } else {
81
+ changeItems.push({ type : type, path : fullTuplePath, value : tuple.val });
82
+ }
83
+ }
84
+ }
85
+ } else {
86
+ if (type == 'REMOVE') {
87
+ changeItems.push({ type : type, path : path, oldValue : value });
88
+ } else {
89
+ changeItems.push({ type : type, path : path, value : value, oldValue : oldValue });
90
+ }
91
+ }
92
+ }
93
+ return changeItems;
94
+ }
95
+ }
@@ -0,0 +1,71 @@
1
+ import { AttributeMap, Op as DeltaOp } from "@shaxpir/quill-delta";
2
+ import { Struct } from "../../util/Struct";
3
+
4
+ export class DeltaOpsModel {
5
+
6
+ private static readonly BLOCK_ATTR_KEYS:any = {
7
+ "align" : true,
8
+ "blockquote" : true,
9
+ "header" : true,
10
+ "indent" : true,
11
+ "list" : true,
12
+ "caption" : true,
13
+ };
14
+
15
+ public static splitOnNewlines(ops:DeltaOp[]):DeltaOp[] {
16
+ let newOps:DeltaOp[] = [];
17
+ for (let i:number = 0, iLen:number = ops.length; i < iLen; i++) {
18
+ let op:DeltaOp = ops[i];
19
+ let text:any = "\n";
20
+ let embed:AttributeMap = null;
21
+ let blockAttr:AttributeMap = null;
22
+ let rangeAttr:AttributeMap = null;
23
+ if (typeof(op.insert) === "string") {
24
+ text = op.insert;
25
+ if (op.hasOwnProperty("attributes")) {
26
+ for (let key in op.attributes) {
27
+ let val:any = (op.attributes as any)[key];
28
+ if (DeltaOpsModel.BLOCK_ATTR_KEYS.hasOwnProperty(key)) {
29
+ if (blockAttr == null) {
30
+ blockAttr = {};
31
+ }
32
+ (blockAttr as any)[key] = val;
33
+ } else {
34
+ if (rangeAttr == null) {
35
+ rangeAttr = {};
36
+ }
37
+ (rangeAttr as any)[key] = val;
38
+ }
39
+ }
40
+
41
+ }
42
+ } else if (op.hasOwnProperty("insert")) {
43
+ embed = Struct.clone(op.insert);
44
+ }
45
+ let parts:string[] = text.split(/(\n)/g);
46
+ let nonEmptyParts:string[] = [];
47
+ for (let j:number = 0, jLen:number = parts.length; j < jLen; j++) {
48
+ let part:string = parts[j];
49
+ if (part.length > 0) {
50
+ nonEmptyParts.push(part);
51
+ }
52
+ }
53
+ parts = nonEmptyParts;
54
+ for (let j:number = 0, jLen:number = parts.length, jLast = jLen - 1; j < jLen; j++) {
55
+ let part:string = parts[j];
56
+ let newOp:DeltaOp = { "insert" : part, "attributes" : {} };
57
+ if (embed !== null) {
58
+ newOp.insert = embed;
59
+ } else if (part === "\n") {
60
+ if (j === jLast && blockAttr != null) {
61
+ newOp.attributes = blockAttr;
62
+ }
63
+ } else if (rangeAttr != null) {
64
+ newOp.attributes = rangeAttr;
65
+ }
66
+ newOps.push(newOp);
67
+ }
68
+ }
69
+ return newOps;
70
+ }
71
+ }