@ultimat3/core 22.5.1 → 22.6.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/package.json +2 -2
- package/src/client-paths.ts +35 -5
- package/src/index.ts +3 -1
- package/src/telemetry.ts +15 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/core",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.6.0",
|
|
4
4
|
"description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"test": "bun test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ultimat3/schema": "22.
|
|
43
|
+
"@ultimat3/schema": "22.6.0"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/client-paths.ts
CHANGED
|
@@ -51,12 +51,42 @@ export function pluralize(word: string): string {
|
|
|
51
51
|
return `${word}s`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* How an action's export name becomes its URL. Declared once per app
|
|
56
|
+
* (`defineApi({ http: { pathStyle } })`), never per action — one app, one rule.
|
|
57
|
+
*
|
|
58
|
+
* | style | `publishPost` | `signIn` | `health` |
|
|
59
|
+
* |---|---|---|---|
|
|
60
|
+
* | `'resource'` (default) | `/api/posts/publish` | `/api/ins/sign` | `/api/healths/invoke` |
|
|
61
|
+
* | `'readable'` | `/api/publish-post` | `/api/sign-in` | `/api/health` |
|
|
62
|
+
*
|
|
63
|
+
* `'resource'` guesses a noun from the words after the first and pluralizes it, which is right for
|
|
64
|
+
* `verbNoun` names and ungrammatical for everything else. `'readable'` guesses nothing: the path IS
|
|
65
|
+
* the name, kebab-cased — the rule `/_x/query/<kebab>` has always followed for reads.
|
|
66
|
+
*/
|
|
67
|
+
export type ActionPathStyle = 'resource' | 'readable';
|
|
68
|
+
|
|
69
|
+
export const ACTION_PATH_STYLES: readonly ActionPathStyle[] = ['resource', 'readable'];
|
|
70
|
+
|
|
71
|
+
/** Every action is served under one prefix, whichever style derives the rest. */
|
|
72
|
+
export const ACTION_PATH_PREFIX = '/api';
|
|
73
|
+
|
|
54
74
|
/**
|
|
55
75
|
* `publishPost` -> `/api/posts/publish`, `updateUserProfile` -> `/api/user-profiles/update`,
|
|
56
|
-
* `checkout` -> `/api/checkouts/invoke` (single-word fallback).
|
|
76
|
+
* `checkout` -> `/api/checkouts/invoke` (single-word fallback) — the `'resource'` style.
|
|
77
|
+
* `'readable'` -> `/api/publish-post`, `/api/update-user-profile`, `/api/checkout`: `verb` is the
|
|
78
|
+
* first word and `resource` the rest, both unpluralized, and neither is part of the path.
|
|
57
79
|
*/
|
|
58
|
-
export function actionRoute(name: string): ActionRoute {
|
|
80
|
+
export function actionRoute(name: string, style: ActionPathStyle = 'resource'): ActionRoute {
|
|
59
81
|
const words = splitWords(name);
|
|
82
|
+
if (style === 'readable') {
|
|
83
|
+
const kebab = words.length === 0 ? 'invoke' : words.join('-');
|
|
84
|
+
return {
|
|
85
|
+
verb: words[0] ?? 'invoke',
|
|
86
|
+
resource: words.length < 2 ? kebab : words.slice(1).join('-'),
|
|
87
|
+
path: `${ACTION_PATH_PREFIX}/${kebab}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
60
90
|
const head = words[0] ?? 'invoke';
|
|
61
91
|
if (words.length < 2) {
|
|
62
92
|
const resource = pluralize(head);
|
|
@@ -68,9 +98,9 @@ export function actionRoute(name: string): ActionRoute {
|
|
|
68
98
|
return { verb: head, resource, path: `/api/${resource}/${head}` };
|
|
69
99
|
}
|
|
70
100
|
|
|
71
|
-
/** The path an action is POSTed to — `actionRoute(name).path`. */
|
|
72
|
-
export function actionPath(name: string): string {
|
|
73
|
-
return actionRoute(name).path;
|
|
101
|
+
/** The path an action is POSTed to — `actionRoute(name, style).path`. */
|
|
102
|
+
export function actionPath(name: string, style: ActionPathStyle = 'resource'): string {
|
|
103
|
+
return actionRoute(name, style).path;
|
|
74
104
|
}
|
|
75
105
|
|
|
76
106
|
/** `liveFeed` -> `/_x/query/live-feed`, read with `GET …?orgId=…`. */
|
package/src/index.ts
CHANGED
|
@@ -68,8 +68,10 @@ export type {
|
|
|
68
68
|
FlightPlan,
|
|
69
69
|
} from './client-flight';
|
|
70
70
|
export { createClientFlight, DEFAULT_CLIENT_RETRY, isTransientFailure } from './client-flight';
|
|
71
|
-
export type { ActionRoute } from './client-paths';
|
|
71
|
+
export type { ActionPathStyle, ActionRoute } from './client-paths';
|
|
72
72
|
export {
|
|
73
|
+
ACTION_PATH_PREFIX,
|
|
74
|
+
ACTION_PATH_STYLES,
|
|
73
75
|
actionPath,
|
|
74
76
|
actionRoute,
|
|
75
77
|
pluralize,
|
package/src/telemetry.ts
CHANGED
|
@@ -69,6 +69,12 @@ export interface Span {
|
|
|
69
69
|
addEvent(name: string, attributes?: SpanAttributes): Span;
|
|
70
70
|
recordError(error: unknown): Span;
|
|
71
71
|
setStatus(code: SpanStatusCode, message?: string): Span;
|
|
72
|
+
/**
|
|
73
|
+
* Rename a span that learned what it is only after it started — an HTTP server span is opened
|
|
74
|
+
* before the router matches, and its name must be the route PATTERN (`GET /r/:token`), never the
|
|
75
|
+
* concrete URL, which carries capability tokens. A no-op once the span has ended.
|
|
76
|
+
*/
|
|
77
|
+
updateName(name: string): Span;
|
|
72
78
|
end(): void;
|
|
73
79
|
}
|
|
74
80
|
|
|
@@ -194,7 +200,8 @@ function inboundParent(parent: SpanContext | undefined): SpanContext | undefined
|
|
|
194
200
|
return parent === undefined || parent.spanId === '' ? undefined : parent;
|
|
195
201
|
}
|
|
196
202
|
|
|
197
|
-
export function startSpan(
|
|
203
|
+
export function startSpan(initialName: string, options?: StartSpanOptions): Span {
|
|
204
|
+
let name = initialName;
|
|
198
205
|
// A live span is what gives an outbound typed call a trace to continue; see the module.
|
|
199
206
|
installTraceHeaders();
|
|
200
207
|
const parent = options?.parent ?? currentSpanContext();
|
|
@@ -219,7 +226,9 @@ export function startSpan(name: string, options?: StartSpanOptions): Span {
|
|
|
219
226
|
let ended = false;
|
|
220
227
|
|
|
221
228
|
const span: Span = {
|
|
222
|
-
name
|
|
229
|
+
get name(): string {
|
|
230
|
+
return name;
|
|
231
|
+
},
|
|
223
232
|
context,
|
|
224
233
|
get ended(): boolean {
|
|
225
234
|
return ended;
|
|
@@ -260,6 +269,10 @@ export function startSpan(name: string, options?: StartSpanOptions): Span {
|
|
|
260
269
|
status = { code, message };
|
|
261
270
|
return span;
|
|
262
271
|
},
|
|
272
|
+
updateName(next) {
|
|
273
|
+
if (!ended) name = next;
|
|
274
|
+
return span;
|
|
275
|
+
},
|
|
263
276
|
end(): void {
|
|
264
277
|
if (ended) return;
|
|
265
278
|
ended = true;
|