@tuturuuu/utils 0.8.0 → 0.9.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.
package/src/effect.ts ADDED
@@ -0,0 +1,259 @@
1
+ import {
2
+ Cause,
3
+ Context,
4
+ Data,
5
+ Duration,
6
+ Effect,
7
+ Either,
8
+ Exit,
9
+ Layer,
10
+ Option,
11
+ Schedule,
12
+ } from 'effect';
13
+
14
+ export {
15
+ Cause,
16
+ Context,
17
+ Data,
18
+ Duration,
19
+ Effect,
20
+ Either,
21
+ Layer,
22
+ Option,
23
+ Schedule,
24
+ };
25
+
26
+ export type TuturuuuEffectErrorContext = Record<string, unknown>;
27
+
28
+ export interface TuturuuuEffectErrorOptions {
29
+ readonly code: string;
30
+ readonly message: string;
31
+ readonly status?: number;
32
+ readonly cause?: unknown;
33
+ readonly context?: TuturuuuEffectErrorContext;
34
+ }
35
+
36
+ export class TuturuuuEffectError extends Data.TaggedError(
37
+ 'TuturuuuEffectError'
38
+ )<TuturuuuEffectErrorOptions> {}
39
+
40
+ export interface SerializedTuturuuuEffectError {
41
+ readonly _tag: 'TuturuuuEffectError';
42
+ readonly code: string;
43
+ readonly message: string;
44
+ readonly status?: number;
45
+ readonly context?: TuturuuuEffectErrorContext;
46
+ readonly cause?: {
47
+ readonly name?: string;
48
+ readonly message: string;
49
+ };
50
+ }
51
+
52
+ export type TuturuuuEffectResult<T> =
53
+ | {
54
+ readonly ok: true;
55
+ readonly data: T;
56
+ }
57
+ | {
58
+ readonly ok: false;
59
+ readonly error: SerializedTuturuuuEffectError;
60
+ };
61
+
62
+ export type DataErrorResult<TData, TError = unknown> = {
63
+ readonly data: TData;
64
+ readonly error: TError | null;
65
+ };
66
+
67
+ export interface TuturuuuEffectRetryOptions<E = unknown> {
68
+ readonly times?: number;
69
+ readonly delay?: Duration.DurationInput;
70
+ readonly while?: (error: E) => boolean;
71
+ }
72
+
73
+ export interface TuturuuuEffectTimeoutOptions
74
+ extends TuturuuuEffectErrorOptions {
75
+ readonly duration: Duration.DurationInput;
76
+ }
77
+
78
+ export interface TuturuuuEffectConcurrencyOptions {
79
+ readonly concurrency?: number | 'unbounded';
80
+ }
81
+
82
+ export const DEFAULT_TUTURUUU_EFFECT_CONCURRENCY = 4;
83
+
84
+ function isRecord(value: unknown): value is Record<string, unknown> {
85
+ return typeof value === 'object' && value !== null;
86
+ }
87
+
88
+ function readStringProperty(
89
+ source: Record<string, unknown> | undefined,
90
+ key: string
91
+ ) {
92
+ const value = source?.[key];
93
+ return typeof value === 'string' && value.trim() ? value : undefined;
94
+ }
95
+
96
+ function readNumberProperty(
97
+ source: Record<string, unknown> | undefined,
98
+ key: string
99
+ ) {
100
+ const value = source?.[key];
101
+ return typeof value === 'number' && Number.isFinite(value)
102
+ ? value
103
+ : undefined;
104
+ }
105
+
106
+ function buildContext(
107
+ source: Record<string, unknown> | undefined,
108
+ fallbackContext: TuturuuuEffectErrorContext | undefined
109
+ ) {
110
+ const context: TuturuuuEffectErrorContext = { ...(fallbackContext ?? {}) };
111
+ const details = readStringProperty(source, 'details');
112
+ const hint = readStringProperty(source, 'hint');
113
+
114
+ if (details) context.details = details;
115
+ if (hint) context.hint = hint;
116
+
117
+ return Object.keys(context).length > 0 ? context : undefined;
118
+ }
119
+
120
+ export function toTuturuuuEffectError(
121
+ error: unknown,
122
+ fallback: TuturuuuEffectErrorOptions
123
+ ) {
124
+ if (error instanceof TuturuuuEffectError) {
125
+ return error;
126
+ }
127
+
128
+ const source = isRecord(error) ? error : undefined;
129
+ const message =
130
+ error instanceof Error && error.message
131
+ ? error.message
132
+ : readStringProperty(source, 'message') || fallback.message;
133
+
134
+ return new TuturuuuEffectError({
135
+ code: readStringProperty(source, 'code') || fallback.code,
136
+ message,
137
+ status: readNumberProperty(source, 'status') ?? fallback.status,
138
+ cause: error,
139
+ context: buildContext(source, fallback.context),
140
+ });
141
+ }
142
+
143
+ export function serializeTuturuuuEffectError(
144
+ error: TuturuuuEffectError
145
+ ): SerializedTuturuuuEffectError {
146
+ return {
147
+ _tag: error._tag,
148
+ code: error.code,
149
+ message: error.message,
150
+ ...(error.status !== undefined ? { status: error.status } : {}),
151
+ ...(error.context ? { context: error.context } : {}),
152
+ ...(error.cause instanceof Error
153
+ ? {
154
+ cause: {
155
+ name: error.cause.name,
156
+ message: error.cause.message,
157
+ },
158
+ }
159
+ : {}),
160
+ };
161
+ }
162
+
163
+ export function fromPromise<T>(
164
+ evaluate: (signal: AbortSignal) => PromiseLike<T>,
165
+ fallback: TuturuuuEffectErrorOptions
166
+ ): Effect.Effect<T, TuturuuuEffectError> {
167
+ return Effect.tryPromise({
168
+ try: evaluate,
169
+ catch: (error) => toTuturuuuEffectError(error, fallback),
170
+ });
171
+ }
172
+
173
+ export function fromDataError<TData, TError = unknown>(
174
+ operation: (
175
+ signal: AbortSignal
176
+ ) => PromiseLike<DataErrorResult<TData, TError>>,
177
+ fallback: TuturuuuEffectErrorOptions
178
+ ): Effect.Effect<TData, TuturuuuEffectError> {
179
+ return Effect.flatMap(fromPromise(operation, fallback), (result) =>
180
+ result.error
181
+ ? Effect.fail(toTuturuuuEffectError(result.error, fallback))
182
+ : Effect.succeed(result.data)
183
+ );
184
+ }
185
+
186
+ export function withTuturuuuRetry<A, E, R>(
187
+ program: Effect.Effect<A, E, R>,
188
+ options: TuturuuuEffectRetryOptions<E> = {}
189
+ ): Effect.Effect<A, E, R> {
190
+ const times = Math.max(0, options.times ?? 2);
191
+
192
+ if (times === 0) {
193
+ return program;
194
+ }
195
+
196
+ return program.pipe(
197
+ Effect.retry({
198
+ ...(options.delay ? { schedule: Schedule.spaced(options.delay) } : {}),
199
+ times,
200
+ ...(options.while ? { while: options.while } : {}),
201
+ })
202
+ );
203
+ }
204
+
205
+ export function withTuturuuuTimeout<A, E, R>(
206
+ program: Effect.Effect<A, E, R>,
207
+ options: TuturuuuEffectTimeoutOptions
208
+ ): Effect.Effect<A, E | TuturuuuEffectError, R> {
209
+ return program.pipe(
210
+ Effect.timeoutFail({
211
+ duration: options.duration,
212
+ onTimeout: () =>
213
+ new TuturuuuEffectError({
214
+ code: options.code,
215
+ message: options.message,
216
+ status: options.status,
217
+ cause: options.cause,
218
+ context: options.context,
219
+ }),
220
+ })
221
+ );
222
+ }
223
+
224
+ export function forEachConcurrently<A, B, E, R>(
225
+ items: Iterable<A>,
226
+ evaluate: (item: A, index: number) => Effect.Effect<B, E, R>,
227
+ options: TuturuuuEffectConcurrencyOptions = {}
228
+ ): Effect.Effect<readonly B[], E, R> {
229
+ return Effect.forEach(items, evaluate, {
230
+ concurrency: options.concurrency ?? DEFAULT_TUTURUUU_EFFECT_CONCURRENCY,
231
+ });
232
+ }
233
+
234
+ export async function runEffectAsResult<T>(
235
+ program: Effect.Effect<T, TuturuuuEffectError, never>
236
+ ): Promise<TuturuuuEffectResult<T>> {
237
+ const exit = await Effect.runPromiseExit(program);
238
+
239
+ if (Exit.isSuccess(exit)) {
240
+ return {
241
+ ok: true,
242
+ data: exit.value,
243
+ };
244
+ }
245
+
246
+ const failure = Cause.failureOption(exit.cause);
247
+ const error = Option.isSome(failure)
248
+ ? failure.value
249
+ : new TuturuuuEffectError({
250
+ code: 'EFFECT_DEFECT',
251
+ message: Cause.pretty(exit.cause),
252
+ cause: exit.cause,
253
+ });
254
+
255
+ return {
256
+ ok: false,
257
+ error: serializeTuturuuuEffectError(error),
258
+ };
259
+ }
@@ -60,6 +60,10 @@ export const PRODUCTION_INTERNAL_APP_DOMAINS = [
60
60
  name: 'finance',
61
61
  url: 'https://finance.tuturuuu.com',
62
62
  },
63
+ {
64
+ name: 'infra',
65
+ url: 'https://infra.tuturuuu.com',
66
+ },
63
67
  {
64
68
  name: 'inventory',
65
69
  url: 'https://inventory.tuturuuu.com',
@@ -68,6 +72,10 @@ export const PRODUCTION_INTERNAL_APP_DOMAINS = [
68
72
  name: 'storefront',
69
73
  url: 'https://storefront.tuturuuu.com',
70
74
  },
75
+ {
76
+ name: 'tanstack-web',
77
+ url: 'https://tanstack.tuturuuu.com',
78
+ },
71
79
  {
72
80
  name: 'track',
73
81
  url: 'https://track.tuturuuu.com',
@@ -143,6 +151,10 @@ export const PORTLESS_INTERNAL_APP_DOMAINS = [
143
151
  name: 'finance',
144
152
  url: getTuturuuuPortlessAppOrigin('finance'),
145
153
  },
154
+ {
155
+ name: 'infra',
156
+ url: getTuturuuuPortlessAppOrigin('infra'),
157
+ },
146
158
  {
147
159
  name: 'inventory',
148
160
  url: getTuturuuuPortlessAppOrigin('inventory'),
@@ -151,6 +163,10 @@ export const PORTLESS_INTERNAL_APP_DOMAINS = [
151
163
  name: 'storefront',
152
164
  url: getTuturuuuPortlessAppOrigin('storefront'),
153
165
  },
166
+ {
167
+ name: 'tanstack-web',
168
+ url: getTuturuuuPortlessAppOrigin('tanstack-web'),
169
+ },
154
170
  {
155
171
  name: 'track',
156
172
  url: getTuturuuuPortlessAppOrigin('track'),
@@ -226,6 +242,10 @@ export const LOCALHOST_INTERNAL_APP_DOMAINS = [
226
242
  name: 'finance',
227
243
  url: 'http://localhost:7808',
228
244
  },
245
+ {
246
+ name: 'infra',
247
+ url: 'http://localhost:7823',
248
+ },
229
249
  {
230
250
  name: 'inventory',
231
251
  url: 'http://localhost:7815',
@@ -234,6 +254,10 @@ export const LOCALHOST_INTERNAL_APP_DOMAINS = [
234
254
  name: 'storefront',
235
255
  url: 'http://localhost:7822',
236
256
  },
257
+ {
258
+ name: 'tanstack-web',
259
+ url: 'http://localhost:7824',
260
+ },
237
261
  {
238
262
  name: 'track',
239
263
  url: 'http://localhost:7810',
@@ -7,7 +7,7 @@ import {
7
7
 
8
8
  describe('platform release metadata', () => {
9
9
  it('uses the centralized shared browser app version', () => {
10
- expect(TUTURUUU_PLATFORM_VERSION).toBe('0.12.0'); // x-release-please-version
10
+ expect(TUTURUUU_PLATFORM_VERSION).toBe('0.13.0'); // x-release-please-version
11
11
  });
12
12
 
13
13
  it('normalizes generated metadata and derives a short hash', () => {
@@ -1,6 +1,6 @@
1
1
  import { PLATFORM_BUILD_METADATA } from './generated/platform-build-metadata';
2
2
 
3
- export const TUTURUUU_PLATFORM_VERSION = '0.12.0'; // x-release-please-version
3
+ export const TUTURUUU_PLATFORM_VERSION = '0.13.0'; // x-release-please-version
4
4
 
5
5
  export type PlatformBuildMetadataInput = {
6
6
  builtAt?: string | null;
package/src/portless.ts CHANGED
@@ -15,6 +15,7 @@ export const TUTURUUU_PORTLESS_APP_ORIGINS = {
15
15
  finance: `https://finance.${TUTURUUU_PORTLESS_ROOT_HOST}`,
16
16
  hive: `https://hive.${TUTURUUU_PORTLESS_ROOT_HOST}`,
17
17
  'hive-realtime': `https://realtime.hive.${TUTURUUU_PORTLESS_ROOT_HOST}`,
18
+ infra: `https://infra.${TUTURUUU_PORTLESS_ROOT_HOST}`,
18
19
  inventory: `https://inventory.${TUTURUUU_PORTLESS_ROOT_HOST}`,
19
20
  learn: `https://learn.${TUTURUUU_PORTLESS_ROOT_HOST}`,
20
21
  mail: `https://mail.${TUTURUUU_PORTLESS_ROOT_HOST}`,
@@ -27,6 +28,7 @@ export const TUTURUUU_PORTLESS_APP_ORIGINS = {
27
28
  rewise: `https://rewise.${TUTURUUU_PORTLESS_ROOT_HOST}`,
28
29
  shortener: `https://shortener.${TUTURUUU_PORTLESS_ROOT_HOST}`,
29
30
  storefront: `https://storefront.${TUTURUUU_PORTLESS_ROOT_HOST}`,
31
+ 'tanstack-web': `https://tanstack.${TUTURUUU_PORTLESS_ROOT_HOST}`,
30
32
  tasks: `https://tasks.${TUTURUUU_PORTLESS_ROOT_HOST}`,
31
33
  teach: `https://teach.${TUTURUUU_PORTLESS_ROOT_HOST}`,
32
34
  track: `https://track.${TUTURUUU_PORTLESS_ROOT_HOST}`,
@@ -165,6 +165,9 @@ export async function invalidateTaskCaches(
165
165
  promises.push(
166
166
  queryClient.invalidateQueries({ queryKey: ['task_lists', boardId] })
167
167
  );
168
+ promises.push(
169
+ queryClient.invalidateQueries({ queryKey: ['kanban-deadline-tasks'] })
170
+ );
168
171
  }
169
172
 
170
173
  promises.push(