@ima-jin/config 0.8.0 → 0.8.13
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/dist/index.cjs +125 -24
- package/dist/index.d.cts +152 -10
- package/dist/index.d.ts +152 -10
- package/dist/index.js +119 -24
- package/package.json +2 -5
- package/src/base-path.ts +6 -6
- package/src/brand.ts +12 -0
- package/src/cors.ts +1 -1
- package/src/dashboard-redirect-middleware.ts +56 -0
- package/src/forest-config.ts +51 -0
- package/src/handles.ts +1 -1
- package/src/index.ts +11 -0
- package/src/node-self.ts +45 -0
- package/src/registry-service-url.ts +54 -0
- package/src/services.ts +18 -2
- package/src/session.ts +28 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ALLOWED_FRAME_ORIGINS: () => ALLOWED_FRAME_ORIGINS,
|
|
24
|
+
APP_DISPLAY_NAME: () => APP_DISPLAY_NAME,
|
|
24
25
|
HANDLE_ALLOWED_CHARS: () => HANDLE_ALLOWED_CHARS,
|
|
25
26
|
HANDLE_ERROR: () => HANDLE_ERROR,
|
|
26
27
|
HANDLE_INPUT_PATTERN: () => HANDLE_INPUT_PATTERN,
|
|
@@ -94,12 +95,15 @@ __export(index_exports, {
|
|
|
94
95
|
eventsCreatePath: () => eventsCreatePath,
|
|
95
96
|
eventsDashboardPath: () => eventsDashboardPath,
|
|
96
97
|
getClientIP: () => getClientIP,
|
|
98
|
+
getForestScopeConfig: () => getForestScopeConfig,
|
|
99
|
+
getNodeSelf: () => getNodeSelf,
|
|
97
100
|
getPort: () => getPort,
|
|
98
101
|
getPublicUrl: () => getPublicUrl,
|
|
99
102
|
getService: () => getService,
|
|
100
103
|
getServiceUrl: () => getServiceUrl,
|
|
101
104
|
getSessionCookieName: () => getSessionCookieName,
|
|
102
105
|
getSessionCookieOptions: () => getSessionCookieOptions,
|
|
106
|
+
hasRegistryServiceUrl: () => hasRegistryServiceUrl,
|
|
103
107
|
healthPath: () => healthPath,
|
|
104
108
|
isAllowedOrigin: () => isAllowedOrigin,
|
|
105
109
|
isReservedHandle: () => isReservedHandle,
|
|
@@ -140,8 +144,10 @@ __export(index_exports, {
|
|
|
140
144
|
rateLimit: () => rateLimit,
|
|
141
145
|
registryDocsPath: () => registryDocsPath,
|
|
142
146
|
registryPath: () => registryPath,
|
|
147
|
+
registryServiceUrl: () => registryServiceUrl,
|
|
143
148
|
servicesByTier: () => servicesByTier,
|
|
144
149
|
servicesByVisibility: () => servicesByVisibility,
|
|
150
|
+
standaloneDashboardMiddleware: () => standaloneDashboardMiddleware,
|
|
145
151
|
subscribePath: () => subscribePath,
|
|
146
152
|
tier1Headers: () => tier1Headers,
|
|
147
153
|
tier2Headers: () => tier2Headers,
|
|
@@ -170,7 +176,7 @@ function corsHeaders(request) {
|
|
|
170
176
|
"Access-Control-Allow-Origin": allowed ? origin : "",
|
|
171
177
|
"Access-Control-Allow-Credentials": "true",
|
|
172
178
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
173
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Caller-DID",
|
|
179
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Caller-DID, X-App-DID, X-App-Authorization",
|
|
174
180
|
"Vary": "Origin"
|
|
175
181
|
};
|
|
176
182
|
}
|
|
@@ -190,26 +196,8 @@ function validateOrigin(request) {
|
|
|
190
196
|
return isAllowedOrigin(origin);
|
|
191
197
|
}
|
|
192
198
|
|
|
193
|
-
// src/
|
|
194
|
-
|
|
195
|
-
const local = process.env.NODE_ENV !== "production" ? " http://localhost:*" : "";
|
|
196
|
-
return `frame-ancestors 'self' ${ALLOWED_FRAME_ORIGINS}${local}`;
|
|
197
|
-
}
|
|
198
|
-
function tier1Headers() {
|
|
199
|
-
return [
|
|
200
|
-
{ key: "Content-Security-Policy", value: "frame-ancestors 'none'" },
|
|
201
|
-
{ key: "X-Frame-Options", value: "DENY" }
|
|
202
|
-
];
|
|
203
|
-
}
|
|
204
|
-
function tier2Headers() {
|
|
205
|
-
return [{ key: "Content-Security-Policy", value: buildFrameAncestors() }];
|
|
206
|
-
}
|
|
207
|
-
function tier3Headers() {
|
|
208
|
-
return [
|
|
209
|
-
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
210
|
-
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }
|
|
211
|
-
];
|
|
212
|
-
}
|
|
199
|
+
// src/dashboard-redirect-middleware.ts
|
|
200
|
+
var import_server2 = require("next/server");
|
|
213
201
|
|
|
214
202
|
// src/services.ts
|
|
215
203
|
var SERVICES = [
|
|
@@ -223,6 +211,11 @@ var SERVICES = [
|
|
|
223
211
|
{ name: "media", label: "Media", icon: "\u{1F4C1}", description: "Files, images, and .fair attribution", devPort: 3e3, prodPort: 7e3, schema: "media", tier: "core", visibility: "authenticated", category: "kernel" },
|
|
224
212
|
{ name: "registry", label: "Registry", icon: "\u{1F4E1}", description: "Service registry and DFOS relay", devPort: 3e3, prodPort: 7e3, schema: "registry", tier: "core", visibility: "public", category: "kernel" },
|
|
225
213
|
{ name: "notify", label: "Notify", icon: "\u{1F514}", description: "Notifications and preferences", devPort: 3e3, prodPort: 7e3, schema: "notify", tier: "core", visibility: "internal", category: "kernel" },
|
|
214
|
+
{ name: "usage", label: "Usage", icon: "\u{1F4CA}", description: "Inference cost ledger \u2014 incurred, billed, drift, signed rollup", devPort: 3e3, prodPort: 7e3, schema: "usage", tier: "core", visibility: "authenticated", category: "kernel" },
|
|
215
|
+
{ name: "calendar", label: "Calendar", icon: "\u{1F4C5}", description: "Temporal entries \u2014 availability, meetings, events, and reminders", devPort: 3e3, prodPort: 7e3, schema: null, tier: "core", visibility: "authenticated", category: "kernel" },
|
|
216
|
+
{ name: "jin", label: "Jin", icon: "\u{1F9ED}", description: "Operator dashboard \u2014 signed approvals, proposals, and usage feed", devPort: 3e3, prodPort: 7e3, schema: "operator", tier: "core", visibility: "internal", category: "kernel" },
|
|
217
|
+
{ name: "google", label: "Google Workspace", icon: "\u{1F4E8}", description: "Gmail/Calendar/Drive/Meet connector \u2014 OAuth2 auth-code, sealed refresh token", devPort: 3e3, prodPort: 7e3, schema: null, tier: "core", visibility: "authenticated", category: "kernel" },
|
|
218
|
+
{ name: "vault", label: "Vault", icon: "\u{1F510}", description: "Sealed secrets, delegation grants, and vault-minted service credentials", devPort: 3e3, prodPort: 7e3, schema: null, tier: "core", visibility: "internal", category: "kernel" },
|
|
226
219
|
// Core apps — separate processes
|
|
227
220
|
{ name: "events", label: "Events", icon: "\u{1F3AB}", description: "Event creation, ticketing, and management", devPort: 3006, prodPort: 7006, schema: "events", tier: "core", visibility: "public", category: "core" },
|
|
228
221
|
// Imajin apps
|
|
@@ -236,7 +229,8 @@ var SERVICES = [
|
|
|
236
229
|
{ name: "github", label: "GitHub", icon: "\u{1F419}", description: "Source code", devPort: 3e3, prodPort: 7e3, schema: "public", tier: "core", visibility: "public", category: "meta", externalUrl: "https://github.com/ima-jin/imajin-ai" },
|
|
237
230
|
{ name: "docs", label: "Docs", icon: "\u{1F4C4}", description: "API documentation", devPort: 3e3, prodPort: 7e3, schema: "public", tier: "core", visibility: "public", category: "meta", wwwPath: "/developer-guide" },
|
|
238
231
|
// Daemon processes — not web apps, no HTTP port exposed
|
|
239
|
-
{ name: "broker-agent", label: "Broker Agent", icon: "\u{1F916}", description: "Telegram broker agent \u2014 conversational surface for social coordination (#1101)", devPort: 0, prodPort: 0, schema: null, tier: "core", visibility: "internal", category: "infrastructure" }
|
|
232
|
+
{ name: "broker-agent", label: "Broker Agent", icon: "\u{1F916}", description: "Telegram broker agent \u2014 conversational surface for social coordination (#1101)", devPort: 0, prodPort: 0, schema: null, tier: "core", visibility: "internal", category: "infrastructure" },
|
|
233
|
+
{ name: "corpus", label: "Corpus", icon: "\u{1F50E}", description: "Per-DID corpus indexing and BM25 search service (#1737)", devPort: 8013, prodPort: 8013, schema: null, tier: "core", visibility: "internal", category: "infrastructure" }
|
|
240
234
|
// Connected apps (separate repos) will use the plugin architecture (#249).
|
|
241
235
|
// Not included here — they authenticate via delegated sessions, not the monorepo manifest.
|
|
242
236
|
];
|
|
@@ -322,6 +316,46 @@ function buildServiceUrlMap(env, mode = "dev") {
|
|
|
322
316
|
return map;
|
|
323
317
|
}
|
|
324
318
|
|
|
319
|
+
// src/dashboard-redirect-middleware.ts
|
|
320
|
+
function standaloneDashboardMiddleware(request, { service, cors = true }) {
|
|
321
|
+
if (request.nextUrl.pathname === "/dashboard") {
|
|
322
|
+
const hubUrl = new URL(`${buildPublicUrlAbsolute("kernel")}/auth/${service}`);
|
|
323
|
+
hubUrl.search = request.nextUrl.search;
|
|
324
|
+
return import_server2.NextResponse.redirect(hubUrl, 308);
|
|
325
|
+
}
|
|
326
|
+
if (!cors) {
|
|
327
|
+
return import_server2.NextResponse.next();
|
|
328
|
+
}
|
|
329
|
+
if (request.method === "OPTIONS") {
|
|
330
|
+
return new import_server2.NextResponse(null, { status: 204, headers: corsHeaders(request) });
|
|
331
|
+
}
|
|
332
|
+
return withCors(import_server2.NextResponse.next(), request);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/security-headers.ts
|
|
336
|
+
function buildFrameAncestors() {
|
|
337
|
+
const local = process.env.NODE_ENV !== "production" ? " http://localhost:*" : "";
|
|
338
|
+
return `frame-ancestors 'self' ${ALLOWED_FRAME_ORIGINS}${local}`;
|
|
339
|
+
}
|
|
340
|
+
function tier1Headers() {
|
|
341
|
+
return [
|
|
342
|
+
{ key: "Content-Security-Policy", value: "frame-ancestors 'none'" },
|
|
343
|
+
{ key: "X-Frame-Options", value: "DENY" }
|
|
344
|
+
];
|
|
345
|
+
}
|
|
346
|
+
function tier2Headers() {
|
|
347
|
+
return [{ key: "Content-Security-Policy", value: buildFrameAncestors() }];
|
|
348
|
+
}
|
|
349
|
+
function tier3Headers() {
|
|
350
|
+
return [
|
|
351
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
352
|
+
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/brand.ts
|
|
357
|
+
var APP_DISPLAY_NAME = "imajin (\u03B1)";
|
|
358
|
+
|
|
325
359
|
// src/session.ts
|
|
326
360
|
function getSessionCookieName(env) {
|
|
327
361
|
const resolved = env ?? (typeof process !== "undefined" && (process.env.IMAJIN_ENV === "dev" || process.env.NODE_ENV === "development") ? "dev" : "prod");
|
|
@@ -333,8 +367,13 @@ function isLocalhost() {
|
|
|
333
367
|
const prefix = process.env.NEXT_PUBLIC_SERVICE_PREFIX ?? "";
|
|
334
368
|
return prefix.includes("localhost");
|
|
335
369
|
}
|
|
370
|
+
function isHostScoped() {
|
|
371
|
+
if (typeof process === "undefined") return false;
|
|
372
|
+
return process.env.SESSION_COOKIE_SCOPE === "host";
|
|
373
|
+
}
|
|
336
374
|
function getSessionCookieOptions(env) {
|
|
337
375
|
const local = isLocalhost();
|
|
376
|
+
const omitDomain = local || isHostScoped();
|
|
338
377
|
return {
|
|
339
378
|
name: getSessionCookieName(env),
|
|
340
379
|
options: {
|
|
@@ -342,7 +381,7 @@ function getSessionCookieOptions(env) {
|
|
|
342
381
|
secure: !local,
|
|
343
382
|
sameSite: local ? "lax" : "none",
|
|
344
383
|
path: "/",
|
|
345
|
-
...
|
|
384
|
+
...omitDomain ? {} : { domain: ".imajin.ai" },
|
|
346
385
|
maxAge: 60 * 60 * 24
|
|
347
386
|
// 24 hours
|
|
348
387
|
}
|
|
@@ -401,11 +440,67 @@ function getClientIP(request) {
|
|
|
401
440
|
return "127.0.0.1";
|
|
402
441
|
}
|
|
403
442
|
|
|
443
|
+
// src/registry-service-url.ts
|
|
444
|
+
var warnedAboutLegacyRegistryUrl = false;
|
|
445
|
+
function registryServiceUrl() {
|
|
446
|
+
if (process.env.REGISTRY_SERVICE_URL) return process.env.REGISTRY_SERVICE_URL;
|
|
447
|
+
if (process.env.REGISTRY_URL) {
|
|
448
|
+
if (!warnedAboutLegacyRegistryUrl) {
|
|
449
|
+
console.warn("REGISTRY_URL is deprecated \u2014 set REGISTRY_SERVICE_URL");
|
|
450
|
+
warnedAboutLegacyRegistryUrl = true;
|
|
451
|
+
}
|
|
452
|
+
return process.env.REGISTRY_URL;
|
|
453
|
+
}
|
|
454
|
+
return `http://localhost:${process.env.PORT || 3e3}/registry`;
|
|
455
|
+
}
|
|
456
|
+
function hasRegistryServiceUrl() {
|
|
457
|
+
return Boolean(process.env.REGISTRY_SERVICE_URL || process.env.REGISTRY_URL);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/node-self.ts
|
|
461
|
+
async function getNodeSelf() {
|
|
462
|
+
const url = `${registryServiceUrl()}/api/node/self`;
|
|
463
|
+
try {
|
|
464
|
+
const res = await fetch(url);
|
|
465
|
+
if (!res.ok) {
|
|
466
|
+
console.warn(`[config] getNodeSelf: registry returned ${res.status} for ${url} \u2014 check REGISTRY_SERVICE_URL`);
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
return await res.json();
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.warn(`[config] getNodeSelf: fetch failed for ${url} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/forest-config.ts
|
|
477
|
+
function profileBaseUrl() {
|
|
478
|
+
if (process.env.PROFILE_SERVICE_URL) return process.env.PROFILE_SERVICE_URL;
|
|
479
|
+
const mode = process.env.NODE_ENV === "production" ? "prod" : "dev";
|
|
480
|
+
const base = getServiceUrl("profile", mode) ?? "http://localhost:3000";
|
|
481
|
+
return `${base}/profile`;
|
|
482
|
+
}
|
|
483
|
+
async function getForestScopeConfig(groupDid) {
|
|
484
|
+
const url = `${profileBaseUrl()}/api/forest/${encodeURIComponent(groupDid)}/config/public`;
|
|
485
|
+
try {
|
|
486
|
+
const res = await fetch(url);
|
|
487
|
+
if (!res.ok) {
|
|
488
|
+
console.warn(`[config] getForestScopeConfig: profile service returned ${res.status} for ${url}`);
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
const data = await res.json();
|
|
492
|
+
return { scopeFeeBps: typeof data?.scopeFeeBps === "number" ? data.scopeFeeBps : null };
|
|
493
|
+
} catch (err) {
|
|
494
|
+
console.warn(`[config] getForestScopeConfig: fetch failed for ${url} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
404
499
|
// src/handles.ts
|
|
405
500
|
var HANDLE_PATTERN = /^[a-z0-9._-]{3,30}$/;
|
|
406
501
|
var HANDLE_EDGE = /^[.-]|[.-]$/;
|
|
407
502
|
var HANDLE_CONSECUTIVE = /[.-]{2}/;
|
|
408
|
-
var HANDLE_INPUT_PATTERN =
|
|
503
|
+
var HANDLE_INPUT_PATTERN = String.raw`[a-z0-9._\-]{3,30}`;
|
|
409
504
|
var HANDLE_ALLOWED_CHARS = /[^a-z0-9._-]/g;
|
|
410
505
|
var RESERVED_HANDLES = /* @__PURE__ */ new Set([
|
|
411
506
|
"admin",
|
|
@@ -551,6 +646,7 @@ var notifySettingsPath = () => `/notify/settings`;
|
|
|
551
646
|
// Annotate the CommonJS export names for ESM import in node:
|
|
552
647
|
0 && (module.exports = {
|
|
553
648
|
ALLOWED_FRAME_ORIGINS,
|
|
649
|
+
APP_DISPLAY_NAME,
|
|
554
650
|
HANDLE_ALLOWED_CHARS,
|
|
555
651
|
HANDLE_ERROR,
|
|
556
652
|
HANDLE_INPUT_PATTERN,
|
|
@@ -624,12 +720,15 @@ var notifySettingsPath = () => `/notify/settings`;
|
|
|
624
720
|
eventsCreatePath,
|
|
625
721
|
eventsDashboardPath,
|
|
626
722
|
getClientIP,
|
|
723
|
+
getForestScopeConfig,
|
|
724
|
+
getNodeSelf,
|
|
627
725
|
getPort,
|
|
628
726
|
getPublicUrl,
|
|
629
727
|
getService,
|
|
630
728
|
getServiceUrl,
|
|
631
729
|
getSessionCookieName,
|
|
632
730
|
getSessionCookieOptions,
|
|
731
|
+
hasRegistryServiceUrl,
|
|
633
732
|
healthPath,
|
|
634
733
|
isAllowedOrigin,
|
|
635
734
|
isReservedHandle,
|
|
@@ -670,8 +769,10 @@ var notifySettingsPath = () => `/notify/settings`;
|
|
|
670
769
|
rateLimit,
|
|
671
770
|
registryDocsPath,
|
|
672
771
|
registryPath,
|
|
772
|
+
registryServiceUrl,
|
|
673
773
|
servicesByTier,
|
|
674
774
|
servicesByVisibility,
|
|
775
|
+
standaloneDashboardMiddleware,
|
|
675
776
|
subscribePath,
|
|
676
777
|
tier1Headers,
|
|
677
778
|
tier2Headers,
|
package/dist/index.d.cts
CHANGED
|
@@ -12,6 +12,39 @@ declare function corsOptions(request: NextRequest): NextResponse;
|
|
|
12
12
|
declare function withCors(response: NextResponse, request: NextRequest): NextResponse;
|
|
13
13
|
declare function validateOrigin(request: NextRequest): boolean;
|
|
14
14
|
|
|
15
|
+
interface StandaloneDashboardMiddlewareOptions {
|
|
16
|
+
/** Service name used to build the hub tab URL (`/auth/<service>`). */
|
|
17
|
+
service: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether to also answer CORS preflight and stamp CORS headers on every
|
|
20
|
+
* other request — matches the app's pre-existing `/api/:path*` CORS
|
|
21
|
+
* middleware (#1525). Defaults to `true`; pass `false` for an app (e.g.
|
|
22
|
+
* dykil) that never had one.
|
|
23
|
+
*/
|
|
24
|
+
cors?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Shared standalone-dashboard-to-hub redirect body (#2332, carved out of
|
|
28
|
+
* #2275), used by every userspace service app's `middleware.ts` so the
|
|
29
|
+
* redirect + CORS pass-through logic isn't duplicated six times over.
|
|
30
|
+
*
|
|
31
|
+
* Each app still declares its own literal `export const config = { matcher:
|
|
32
|
+
* [...] }` — Next.js statically extracts `matcher` from the middleware
|
|
33
|
+
* file's AST at build time (see
|
|
34
|
+
* `next/dist/build/analysis/extract-const-value.js`: it only understands
|
|
35
|
+
* literal object/array/string/etc. expressions, not a function call or a
|
|
36
|
+
* destructured import) — so the matcher itself can't be factored out here
|
|
37
|
+
* without Next silently falling back to its default, unrestricted matcher.
|
|
38
|
+
* Only the function *body* is shared.
|
|
39
|
+
*
|
|
40
|
+
* The redirect is unconditional: a 308 to `/auth/<service>`, preserving the
|
|
41
|
+
* query string. The embedded rendering path `<ServiceEmbed>` loads
|
|
42
|
+
* (`?embed=hub&did=...`, see `apps/kernel/app/auth/lib/service-registry.ts`'s
|
|
43
|
+
* `buildEmbedSrc`) never reaches this function at all — it's excluded
|
|
44
|
+
* declaratively by each app's `matcher.missing` clause.
|
|
45
|
+
*/
|
|
46
|
+
declare function standaloneDashboardMiddleware(request: NextRequest, { service, cors }: StandaloneDashboardMiddlewareOptions): NextResponse;
|
|
47
|
+
|
|
15
48
|
type HeaderEntry = {
|
|
16
49
|
key: string;
|
|
17
50
|
value: string;
|
|
@@ -35,9 +68,32 @@ declare function tier2Headers(): HeaderEntry[];
|
|
|
35
68
|
declare function tier3Headers(): HeaderEntry[];
|
|
36
69
|
|
|
37
70
|
/**
|
|
38
|
-
*
|
|
71
|
+
* Human-facing product display name.
|
|
72
|
+
*
|
|
73
|
+
* This is the ONLY place the "(α)" (alpha) stage marker lives — flip it here
|
|
74
|
+
* (e.g. to "(β)" or drop it for GA) and every consumer (BuildInfo, nav bars,
|
|
75
|
+
* page titles/metadata, auth screens, email templates, etc.) picks it up.
|
|
39
76
|
*
|
|
40
|
-
*
|
|
77
|
+
* Do NOT use this for DIDs, URLs, package names, event names, localStorage
|
|
78
|
+
* keys, env vars, or any other code identifier — those stay as literal
|
|
79
|
+
* 'imajin' strings. This constant is display-text only.
|
|
80
|
+
*/
|
|
81
|
+
declare const APP_DISPLAY_NAME = "imajin (\u03B1)";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Canonical service manifest — infra/build-time topology for all Imajin
|
|
85
|
+
* services (dev/prod ports, Postgres schema, deployment tier, Caddy/pm2
|
|
86
|
+
* routing, docs sync, `/registry/api/specs`). These consumers run without a
|
|
87
|
+
* live identity/DB context (build scripts, edge config, port-based
|
|
88
|
+
* routing), so this stays a static, synchronously-importable array.
|
|
89
|
+
*
|
|
90
|
+
* NOT the nav authority (#2425): which apps an authenticated identity sees
|
|
91
|
+
* in the kernel hub (auth submenu, launcher, home tiles) is now derived
|
|
92
|
+
* from `registry.apps` (#1990) via `apps/kernel/src/lib/kernel/app-nav.ts`,
|
|
93
|
+
* not from this file — a disabled/pruned app disappears from nav by
|
|
94
|
+
* flipping registry state, independent of whether it still has a row here
|
|
95
|
+
* for port/Caddy purposes. Consumers: registry specs, Caddy config, pm2
|
|
96
|
+
* ecosystem, docs sync.
|
|
41
97
|
*/
|
|
42
98
|
type ServiceTier = "core" | "imajin";
|
|
43
99
|
type ServiceVisibility = "public" | "authenticated" | "creator" | "internal";
|
|
@@ -138,6 +194,9 @@ declare const SESSION_COOKIE_NAME: string;
|
|
|
138
194
|
*
|
|
139
195
|
* Localhost-aware: when SERVICE_PREFIX contains "localhost", uses
|
|
140
196
|
* settings compatible with HTTP on localhost (no domain, not secure, lax sameSite).
|
|
197
|
+
*
|
|
198
|
+
* See {@link isHostScoped} for the `SESSION_COOKIE_SCOPE=host` opt-in that
|
|
199
|
+
* narrows the deployed cookie to host-only instead of `.imajin.ai`.
|
|
141
200
|
*/
|
|
142
201
|
declare function getSessionCookieOptions(env?: "dev" | "prod"): {
|
|
143
202
|
name: string;
|
|
@@ -189,17 +248,17 @@ declare function apiUrl(path: string): string;
|
|
|
189
248
|
*
|
|
190
249
|
* Absolute URLs (http://, https://) are passed through unchanged.
|
|
191
250
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
251
|
+
* For server-side service-to-service calls, forward the X-Correlation-Id header
|
|
252
|
+
* so the full request chain shares one ID. apiFetch is browser-side too, so it
|
|
253
|
+
* can't read headers here automatically — pass it explicitly via init.headers
|
|
254
|
+
* when calling from a withLogger handler:
|
|
196
255
|
*
|
|
197
256
|
* apiFetch('/api/something', {
|
|
198
257
|
* headers: { 'x-correlation-id': correlationId },
|
|
199
258
|
* });
|
|
200
259
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
260
|
+
* see: automatic correlationId forwarding on outbound apiFetch/fetch calls from
|
|
261
|
+
* withLogger handlers is not yet wired up.
|
|
203
262
|
*/
|
|
204
263
|
declare function apiFetch(path: string, init?: RequestInit): Promise<Response>;
|
|
205
264
|
|
|
@@ -213,6 +272,89 @@ declare function rateLimit(key: string, limit: number, windowMs: number): {
|
|
|
213
272
|
};
|
|
214
273
|
declare function getClientIP(request: Request): string;
|
|
215
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Minimal public node metadata returned by GET /registry/api/node/self.
|
|
277
|
+
* Mirrors the subset of `relay.relay_config` the registry service exposes
|
|
278
|
+
* publicly — never the private config (profile artifact JWS, etc).
|
|
279
|
+
*/
|
|
280
|
+
interface NodeSelfInfo {
|
|
281
|
+
/** This node's own DID (did:imajin:...). */
|
|
282
|
+
did: string;
|
|
283
|
+
/** DID of the human/entity operating this node, if configured. */
|
|
284
|
+
nodeOperatorDid: string | null;
|
|
285
|
+
/** Node fee, in basis points, applied to .fair settlements on this node. */
|
|
286
|
+
nodeFeeBps: number | null;
|
|
287
|
+
/** Buyer credit, in basis points, applied to .fair settlements on this node. */
|
|
288
|
+
buyerCreditBps: number | null;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Fetch this node's public identity + .fair fee config from the registry
|
|
292
|
+
* service (`GET /registry/api/node/self`, #2000).
|
|
293
|
+
*
|
|
294
|
+
* Replaces raw `relay.relay_config` SQL reads that were duplicated across
|
|
295
|
+
* coffee, learn, events, and market (audit item 8 of #1983). Returns null on
|
|
296
|
+
* any failure — network error, non-2xx response (e.g. 503 when the node
|
|
297
|
+
* identity hasn't been bootstrapped yet) — so callers can fall back to
|
|
298
|
+
* defaults exactly as they did when the raw SQL row was missing. A non-2xx
|
|
299
|
+
* response is logged with the URL actually hit (no secrets) so a
|
|
300
|
+
* misconfigured REGISTRY_SERVICE_URL prefix is visible instead of failing
|
|
301
|
+
* silently (#2046).
|
|
302
|
+
*/
|
|
303
|
+
declare function getNodeSelf(): Promise<NodeSelfInfo | null>;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Single resolver for the registry service's base URL (#2061 — kernel was
|
|
307
|
+
* the last `REGISTRY_URL` holdout after #2048 established the
|
|
308
|
+
* `*_SERVICE_URL` convention).
|
|
309
|
+
*
|
|
310
|
+
* The returned value always includes the `/registry` path prefix, like
|
|
311
|
+
* every other `*_SERVICE_URL` (#2046) — callers append only the endpoint
|
|
312
|
+
* path (e.g. `/api/node/self`, `/relay/operations`).
|
|
313
|
+
*
|
|
314
|
+
* Precedence:
|
|
315
|
+
* 1. `REGISTRY_SERVICE_URL` — the current convention.
|
|
316
|
+
* 2. `REGISTRY_URL` — deprecated kernel-only legacy name. Used if set,
|
|
317
|
+
* but logs a one-time deprecation warning per process.
|
|
318
|
+
* 3. `http://localhost:${PORT || 3000}/registry` — the mode-aware
|
|
319
|
+
* fallback (PORT is 3000 in dev, 7000 in prod) that
|
|
320
|
+
* `relay-well-known.ts` already used before this consolidation.
|
|
321
|
+
*/
|
|
322
|
+
declare function registryServiceUrl(): string;
|
|
323
|
+
/**
|
|
324
|
+
* True when the registry URL is explicitly configured via either the
|
|
325
|
+
* current (`REGISTRY_SERVICE_URL`) or deprecated (`REGISTRY_URL`) env var.
|
|
326
|
+
*
|
|
327
|
+
* `registryServiceUrl()` always returns a value (it falls back to a
|
|
328
|
+
* localhost guess), so callers that previously guarded on
|
|
329
|
+
* `!process.env.REGISTRY_URL` to skip/no-op when the registry isn't
|
|
330
|
+
* configured should check this instead.
|
|
331
|
+
*/
|
|
332
|
+
declare function hasRegistryServiceUrl(): boolean;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Public forest-group (scope) config exposed by
|
|
336
|
+
* `GET /api/forest/{groupDid}/config/public` on the profile service. Only
|
|
337
|
+
* the field relevant to `.fair` fee-split calculation is modeled here — the
|
|
338
|
+
* rest of the config (enabledServices, landingService, etc.) is a
|
|
339
|
+
* community-settings concern, not a shared SDK concern.
|
|
340
|
+
*/
|
|
341
|
+
interface ForestScopeConfig {
|
|
342
|
+
/** Group's configured revenue-share fee, in basis points (0-500). Null if unset. */
|
|
343
|
+
scopeFeeBps: number | null;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Fetch a forest group's public scope-fee config from the profile service
|
|
347
|
+
* (`GET /api/forest/{groupDid}/config/public`, documented in
|
|
348
|
+
* `apps/kernel/api-spec/profile.yaml`).
|
|
349
|
+
*
|
|
350
|
+
* Replaces raw `profile.forest_config` SQL reads that were duplicated
|
|
351
|
+
* across coffee, learn, and market (audit item 9 of #1983, #2001). Returns
|
|
352
|
+
* null on any failure — network error, non-2xx response — so callers can
|
|
353
|
+
* fall back to the same `scopeFeeBps: null` default they used when the raw
|
|
354
|
+
* SQL row was missing.
|
|
355
|
+
*/
|
|
356
|
+
declare function getForestScopeConfig(groupDid: string): Promise<ForestScopeConfig | null>;
|
|
357
|
+
|
|
216
358
|
/**
|
|
217
359
|
* Handle validation and normalization.
|
|
218
360
|
*
|
|
@@ -223,7 +365,7 @@ declare function getClientIP(request: Request): string;
|
|
|
223
365
|
/** Regex: the full allowed character set, length enforced */
|
|
224
366
|
declare const HANDLE_PATTERN: RegExp;
|
|
225
367
|
/** HTML input pattern attribute (no anchors, no flags) */
|
|
226
|
-
declare const HANDLE_INPUT_PATTERN
|
|
368
|
+
declare const HANDLE_INPUT_PATTERN: string;
|
|
227
369
|
/** Characters allowed in handles — use in onChange filters */
|
|
228
370
|
declare const HANDLE_ALLOWED_CHARS: RegExp;
|
|
229
371
|
/** Reserved handles that cannot be claimed */
|
|
@@ -340,4 +482,4 @@ declare const projectPath: () => string;
|
|
|
340
482
|
declare const notifyPath: () => string;
|
|
341
483
|
declare const notifySettingsPath: () => string;
|
|
342
484
|
|
|
343
|
-
export { ALLOWED_FRAME_ORIGINS, HANDLE_ALLOWED_CHARS, HANDLE_ERROR, HANDLE_INPUT_PATTERN, HANDLE_PATTERN, RESERVED_HANDLES, SERVICES, SERVICE_NAMES, SESSION_COOKIE_NAME, type ServiceCategory, type ServiceDefinition, type ServiceTier, type ServiceVisibility, apiFetch, apiUrl, articleAuthorPath, articlePath, articlesPath, authAgentsPath, authAppsPath, authAttestationsPath, authAuthorizePath, authDeveloperAppPath, authDeveloperAppsPath, authGroupSettingsPath, authGroupsPath, authLoginPath, authMembersPath, authNewGroupPath, authNewStubPath, authNotificationsPath, authOnboardPath, authRegisterPath, authSecurityPath, authSettingsPath, authStubsPath, bugsAdminPath, bugsPath, buildPath, buildPublicUrl, buildPublicUrlAbsolute, buildServiceUrlMap, bumpPath, chatConversationPath, chatConversationsPath, chatPath, coffeeDashboardPath, coffeeEditPath, coffeeHandlePath, coffeeSuccessPath, connectionsInvitePath, connectionsPath, connectionsPodPath, corsHeaders, corsOptions, developerGuidePath, docsPath, dykilCreatePath, dykilDashboardPath, dykilEmbedPath, dykilHandlePath, dykilResultsPath, dykilSurveyPath, eventAdminPath, eventCheckoutSuccessPath, eventEditPath, eventEditUrl, eventMyTicketsPath, eventMyTicketsUrl, eventPath, eventRegisterPath, eventRegisterUrl, eventUrl, eventsAdminListPath, eventsCheckoutSuccessPath, eventsCreatePath, eventsDashboardPath, getClientIP, getPort, getPublicUrl, getService, getServiceUrl, getSessionCookieName, getSessionCookieOptions, healthPath, isAllowedOrigin, isReservedHandle, isValidHandle, learnCourseDashboardPath, learnCourseLessonPath, learnCoursePath, learnCoursePresentPath, learnCourseStudentsPath, learnDashboardPath, learnHandlePath, linksDashboardPath, linksEditPath, linksHandlePath, marketCheckoutSuccessPath, marketDashboardPath, marketListingEditPath, marketListingPath, marketNewListingPath, marketSellerPath, marketSettingsPath, mediaPath, normalizeHandleInput, notifyPath, notifySettingsPath, payHistoryPath, payPath, payPayoutsPath, payTopupPath, payTopupSuccessPath, privacyPath, profileEditPath, profileLoginPath, profilePath, profileRegisterPath, profileUrl, projectPath, rateLimit, registryDocsPath, registryPath, servicesByTier, servicesByVisibility, subscribePath, tier1Headers, tier2Headers, tier3Headers, validateOrigin, whitepaperPath, withCors };
|
|
485
|
+
export { ALLOWED_FRAME_ORIGINS, APP_DISPLAY_NAME, type ForestScopeConfig, HANDLE_ALLOWED_CHARS, HANDLE_ERROR, HANDLE_INPUT_PATTERN, HANDLE_PATTERN, type NodeSelfInfo, RESERVED_HANDLES, SERVICES, SERVICE_NAMES, SESSION_COOKIE_NAME, type ServiceCategory, type ServiceDefinition, type ServiceTier, type ServiceVisibility, type StandaloneDashboardMiddlewareOptions, apiFetch, apiUrl, articleAuthorPath, articlePath, articlesPath, authAgentsPath, authAppsPath, authAttestationsPath, authAuthorizePath, authDeveloperAppPath, authDeveloperAppsPath, authGroupSettingsPath, authGroupsPath, authLoginPath, authMembersPath, authNewGroupPath, authNewStubPath, authNotificationsPath, authOnboardPath, authRegisterPath, authSecurityPath, authSettingsPath, authStubsPath, bugsAdminPath, bugsPath, buildPath, buildPublicUrl, buildPublicUrlAbsolute, buildServiceUrlMap, bumpPath, chatConversationPath, chatConversationsPath, chatPath, coffeeDashboardPath, coffeeEditPath, coffeeHandlePath, coffeeSuccessPath, connectionsInvitePath, connectionsPath, connectionsPodPath, corsHeaders, corsOptions, developerGuidePath, docsPath, dykilCreatePath, dykilDashboardPath, dykilEmbedPath, dykilHandlePath, dykilResultsPath, dykilSurveyPath, eventAdminPath, eventCheckoutSuccessPath, eventEditPath, eventEditUrl, eventMyTicketsPath, eventMyTicketsUrl, eventPath, eventRegisterPath, eventRegisterUrl, eventUrl, eventsAdminListPath, eventsCheckoutSuccessPath, eventsCreatePath, eventsDashboardPath, getClientIP, getForestScopeConfig, getNodeSelf, getPort, getPublicUrl, getService, getServiceUrl, getSessionCookieName, getSessionCookieOptions, hasRegistryServiceUrl, healthPath, isAllowedOrigin, isReservedHandle, isValidHandle, learnCourseDashboardPath, learnCourseLessonPath, learnCoursePath, learnCoursePresentPath, learnCourseStudentsPath, learnDashboardPath, learnHandlePath, linksDashboardPath, linksEditPath, linksHandlePath, marketCheckoutSuccessPath, marketDashboardPath, marketListingEditPath, marketListingPath, marketNewListingPath, marketSellerPath, marketSettingsPath, mediaPath, normalizeHandleInput, notifyPath, notifySettingsPath, payHistoryPath, payPath, payPayoutsPath, payTopupPath, payTopupSuccessPath, privacyPath, profileEditPath, profileLoginPath, profilePath, profileRegisterPath, profileUrl, projectPath, rateLimit, registryDocsPath, registryPath, registryServiceUrl, servicesByTier, servicesByVisibility, standaloneDashboardMiddleware, subscribePath, tier1Headers, tier2Headers, tier3Headers, validateOrigin, whitepaperPath, withCors };
|