@tuturuuu/utils 0.8.0 → 0.10.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/CHANGELOG.md +21 -0
- package/biome.json +1 -1
- package/package.json +17 -15
- package/src/__tests__/api-proxy-guard.test.ts +728 -71
- package/src/__tests__/app-url.test.ts +2 -0
- package/src/__tests__/task-description-codec.test.ts +110 -0
- package/src/abuse-protection/__tests__/backend-rate-limit.test.ts +10 -45
- package/src/abuse-protection/__tests__/edge-trust.test.ts +99 -0
- package/src/abuse-protection/__tests__/index.test.ts +136 -0
- package/src/abuse-protection/backend-rate-limit.ts +5 -4
- package/src/abuse-protection/edge-trust.ts +141 -6
- package/src/abuse-protection/index.ts +73 -40
- package/src/api-proxy-guard.ts +202 -43
- package/src/currencies.test.ts +33 -0
- package/src/currencies.ts +28 -5
- package/src/effect.test.ts +247 -0
- package/src/effect.ts +259 -0
- package/src/internal-domains.ts +24 -0
- package/src/platform-release.test.ts +1 -1
- package/src/platform-release.ts +1 -1
- package/src/portless.ts +2 -0
- package/src/task-description-codec.ts +457 -0
- package/src/task-helper/task-operations.ts +3 -0
- package/src/yjs-task-description.ts +13 -21
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
Effect,
|
|
4
|
+
forEachConcurrently,
|
|
5
|
+
fromDataError,
|
|
6
|
+
fromPromise,
|
|
7
|
+
runEffectAsResult,
|
|
8
|
+
serializeTuturuuuEffectError,
|
|
9
|
+
TuturuuuEffectError,
|
|
10
|
+
toTuturuuuEffectError,
|
|
11
|
+
withTuturuuuRetry,
|
|
12
|
+
withTuturuuuTimeout,
|
|
13
|
+
} from './effect';
|
|
14
|
+
|
|
15
|
+
describe('@tuturuuu/utils/effect', () => {
|
|
16
|
+
it('creates tagged Tuturuuu errors with metadata', () => {
|
|
17
|
+
const error = new TuturuuuEffectError({
|
|
18
|
+
code: 'TEST_ERROR',
|
|
19
|
+
message: 'Test failed',
|
|
20
|
+
status: 418,
|
|
21
|
+
context: { feature: 'effect' },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(error._tag).toBe('TuturuuuEffectError');
|
|
25
|
+
expect(error.message).toBe('Test failed');
|
|
26
|
+
expect(serializeTuturuuuEffectError(error)).toEqual({
|
|
27
|
+
_tag: 'TuturuuuEffectError',
|
|
28
|
+
code: 'TEST_ERROR',
|
|
29
|
+
message: 'Test failed',
|
|
30
|
+
status: 418,
|
|
31
|
+
context: { feature: 'effect' },
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('normalizes unknown errors with fallback metadata', () => {
|
|
36
|
+
const error = toTuturuuuEffectError(new Error('Boom'), {
|
|
37
|
+
code: 'FALLBACK',
|
|
38
|
+
message: 'Fallback message',
|
|
39
|
+
status: 500,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(error).toBeInstanceOf(TuturuuuEffectError);
|
|
43
|
+
expect(error.code).toBe('FALLBACK');
|
|
44
|
+
expect(error.message).toBe('Boom');
|
|
45
|
+
expect(error.status).toBe(500);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('converts data/error style operations into successful effects', async () => {
|
|
49
|
+
const result = await Effect.runPromise(
|
|
50
|
+
fromDataError(
|
|
51
|
+
() => Promise.resolve({ data: { id: 'row-1' }, error: null }),
|
|
52
|
+
{
|
|
53
|
+
code: 'DATA_ERROR',
|
|
54
|
+
message: 'Data read failed',
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(result).toEqual({ id: 'row-1' });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('converts data/error style failures into typed errors', async () => {
|
|
63
|
+
const result = await runEffectAsResult(
|
|
64
|
+
fromDataError(
|
|
65
|
+
() =>
|
|
66
|
+
Promise.resolve({
|
|
67
|
+
data: null,
|
|
68
|
+
error: {
|
|
69
|
+
code: 'PGRST116',
|
|
70
|
+
message: 'No row found',
|
|
71
|
+
details: 'Expected one row',
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
{
|
|
75
|
+
code: 'DATA_ERROR',
|
|
76
|
+
message: 'Data read failed',
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual({
|
|
82
|
+
ok: false,
|
|
83
|
+
error: {
|
|
84
|
+
_tag: 'TuturuuuEffectError',
|
|
85
|
+
code: 'PGRST116',
|
|
86
|
+
message: 'No row found',
|
|
87
|
+
context: { details: 'Expected one row' },
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('converts rejected promises into typed errors', async () => {
|
|
93
|
+
const result = await runEffectAsResult(
|
|
94
|
+
fromPromise(() => Promise.reject(new Error('Network failed')), {
|
|
95
|
+
code: 'NETWORK_FAILED',
|
|
96
|
+
message: 'Request failed',
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(result.ok).toBe(false);
|
|
101
|
+
if (!result.ok) {
|
|
102
|
+
expect(result.error).toMatchObject({
|
|
103
|
+
_tag: 'TuturuuuEffectError',
|
|
104
|
+
code: 'NETWORK_FAILED',
|
|
105
|
+
message: 'Network failed',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('returns success and failure result shapes', async () => {
|
|
111
|
+
await expect(runEffectAsResult(Effect.succeed('ok'))).resolves.toEqual({
|
|
112
|
+
ok: true,
|
|
113
|
+
data: 'ok',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await expect(
|
|
117
|
+
runEffectAsResult(
|
|
118
|
+
Effect.fail(
|
|
119
|
+
new TuturuuuEffectError({
|
|
120
|
+
code: 'EXPECTED_FAILURE',
|
|
121
|
+
message: 'Expected failure',
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
).resolves.toEqual({
|
|
126
|
+
ok: false,
|
|
127
|
+
error: {
|
|
128
|
+
_tag: 'TuturuuuEffectError',
|
|
129
|
+
code: 'EXPECTED_FAILURE',
|
|
130
|
+
message: 'Expected failure',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('retries expected failures with a typed retry predicate', async () => {
|
|
136
|
+
let attempts = 0;
|
|
137
|
+
const program = Effect.flatMap(
|
|
138
|
+
Effect.sync(() => {
|
|
139
|
+
attempts += 1;
|
|
140
|
+
return attempts;
|
|
141
|
+
}),
|
|
142
|
+
(attempt) =>
|
|
143
|
+
attempt < 3
|
|
144
|
+
? Effect.fail(
|
|
145
|
+
new TuturuuuEffectError({
|
|
146
|
+
code: 'TRANSIENT_FAILURE',
|
|
147
|
+
message: 'Try again',
|
|
148
|
+
})
|
|
149
|
+
)
|
|
150
|
+
: Effect.succeed('ready')
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
await expect(
|
|
154
|
+
Effect.runPromise(
|
|
155
|
+
withTuturuuuRetry(program, {
|
|
156
|
+
times: 2,
|
|
157
|
+
while: (error) => error.code === 'TRANSIENT_FAILURE',
|
|
158
|
+
})
|
|
159
|
+
)
|
|
160
|
+
).resolves.toBe('ready');
|
|
161
|
+
expect(attempts).toBe(3);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('does not retry when the retry predicate rejects the error', async () => {
|
|
165
|
+
let attempts = 0;
|
|
166
|
+
|
|
167
|
+
const result = await runEffectAsResult(
|
|
168
|
+
withTuturuuuRetry(
|
|
169
|
+
Effect.flatMap(
|
|
170
|
+
Effect.sync(() => {
|
|
171
|
+
attempts += 1;
|
|
172
|
+
}),
|
|
173
|
+
() =>
|
|
174
|
+
Effect.fail(
|
|
175
|
+
new TuturuuuEffectError({
|
|
176
|
+
code: 'PERMANENT_FAILURE',
|
|
177
|
+
message: 'Do not retry',
|
|
178
|
+
})
|
|
179
|
+
)
|
|
180
|
+
),
|
|
181
|
+
{
|
|
182
|
+
times: 3,
|
|
183
|
+
while: (error) => error.code === 'TRANSIENT_FAILURE',
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(attempts).toBe(1);
|
|
189
|
+
expect(result).toEqual({
|
|
190
|
+
ok: false,
|
|
191
|
+
error: {
|
|
192
|
+
_tag: 'TuturuuuEffectError',
|
|
193
|
+
code: 'PERMANENT_FAILURE',
|
|
194
|
+
message: 'Do not retry',
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('converts timeouts into Tuturuuu effect errors', async () => {
|
|
200
|
+
const result = await runEffectAsResult(
|
|
201
|
+
withTuturuuuTimeout(Effect.never, {
|
|
202
|
+
code: 'TIMED_OUT',
|
|
203
|
+
duration: 0,
|
|
204
|
+
message: 'Operation timed out',
|
|
205
|
+
context: { operation: 'test' },
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
expect(result).toEqual({
|
|
210
|
+
ok: false,
|
|
211
|
+
error: {
|
|
212
|
+
_tag: 'TuturuuuEffectError',
|
|
213
|
+
code: 'TIMED_OUT',
|
|
214
|
+
message: 'Operation timed out',
|
|
215
|
+
context: { operation: 'test' },
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('runs effectful collections with bounded concurrency', async () => {
|
|
221
|
+
let active = 0;
|
|
222
|
+
let maxActive = 0;
|
|
223
|
+
|
|
224
|
+
const result = await Effect.runPromise(
|
|
225
|
+
forEachConcurrently(
|
|
226
|
+
[1, 2, 3, 4],
|
|
227
|
+
(item) =>
|
|
228
|
+
Effect.promise(
|
|
229
|
+
() =>
|
|
230
|
+
new Promise<number>((resolve) => {
|
|
231
|
+
active += 1;
|
|
232
|
+
maxActive = Math.max(maxActive, active);
|
|
233
|
+
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
active -= 1;
|
|
236
|
+
resolve(item * 2);
|
|
237
|
+
}, 5);
|
|
238
|
+
})
|
|
239
|
+
),
|
|
240
|
+
{ concurrency: 2 }
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(result).toEqual([2, 4, 6, 8]);
|
|
245
|
+
expect(maxActive).toBeLessThanOrEqual(2);
|
|
246
|
+
});
|
|
247
|
+
});
|
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
|
+
}
|
package/src/internal-domains.ts
CHANGED
|
@@ -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.
|
|
10
|
+
expect(TUTURUUU_PLATFORM_VERSION).toBe('0.14.0'); // x-release-please-version
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it('normalizes generated metadata and derives a short hash', () => {
|
package/src/platform-release.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PLATFORM_BUILD_METADATA } from './generated/platform-build-metadata';
|
|
2
2
|
|
|
3
|
-
export const TUTURUUU_PLATFORM_VERSION = '0.
|
|
3
|
+
export const TUTURUUU_PLATFORM_VERSION = '0.14.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}`,
|