akanjs 3.0.0-alpha.2 → 3.0.0-alpha.4
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/base/symbols.ts +0 -1
- package/client/cn.ts +7 -0
- package/client/csrTypes.ts +2 -0
- package/client/frameDebug.ts +3 -2
- package/constant/cascadePaths.ts +8 -79
- package/document/database.ts +1 -2
- package/document/databaseRegistry.ts +2 -2
- package/document/into.ts +6 -10
- package/package.json +1 -1
- package/server/SSR_MEMORY_DIAGNOSIS.md +2 -19
- package/server/cachePolicy.ts +12 -114
- package/server/di/diLifecycle.ts +10 -8
- package/server/processMetricsCollector.ts +0 -4
- package/server/resolver/database.resolver.ts +3 -23
- package/server/resolver/index.ts +0 -1
- package/server/resolver/service.resolver.ts +25 -9
- package/server/rscWorker.tsx +4 -32
- package/server/rscWorkerHost.ts +12 -61
- package/server/ssrFromRscRenderer.tsx +2 -2
- package/server/webRouter.ts +96 -9
- package/service/ipcTypes.ts +0 -25
- package/service/predefinedAdaptor/database.adaptor.ts +3 -3
- package/service/serve.ts +1 -5
- package/service/types.ts +0 -2
- package/types/base/symbols.d.ts +0 -1
- package/types/client/cn.d.ts +5 -0
- package/types/client/csrTypes.d.ts +2 -0
- package/types/constant/cascadePaths.d.ts +4 -22
- package/types/document/database.d.ts +1 -2
- package/types/document/databaseRegistry.d.ts +2 -2
- package/types/document/into.d.ts +5 -6
- package/types/server/cachePolicy.d.ts +2 -33
- package/types/server/resolver/database.resolver.d.ts +2 -6
- package/types/server/resolver/index.d.ts +0 -1
- package/types/server/resolver/service.resolver.d.ts +3 -3
- package/types/server/rscWorkerHost.d.ts +0 -13
- package/types/service/ipcTypes.d.ts +0 -24
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +2 -2
- package/types/service/types.d.ts +1 -2
- package/types/ui/Layout/BottomInset.d.ts +2 -1
- package/types/ui/Layout/index.d.ts +1 -1
- package/types/ui/Signal/style.d.ts +1 -1
- package/types/ui/UiOverride/context.d.ts +12 -2
- package/types/ui/recipe/badgeRecipe.d.ts +7 -3
- package/types/ui/recipe/buttonRecipe.d.ts +7 -3
- package/types/ui/recipe/inputRecipe.d.ts +5 -1
- package/types/webkit/useCsrValues.d.ts +3 -3
- package/types/webkit/useFrameRuntime.d.ts +6 -1
- package/ui/Badge.tsx +2 -2
- package/ui/Button.tsx +3 -1
- package/ui/Constant/Doc.tsx +1 -1
- package/ui/Data/ListContainer.tsx +1 -1
- package/ui/DatePicker.tsx +4 -3
- package/ui/DraggableList.tsx +3 -1
- package/ui/Dropdown.tsx +4 -2
- package/ui/Field.tsx +17 -9
- package/ui/Input.tsx +7 -5
- package/ui/Layout/BottomInset.tsx +6 -1
- package/ui/Loading/ProgressBar.tsx +8 -1
- package/ui/Menu.tsx +7 -8
- package/ui/Model/SureToRemove.tsx +2 -1
- package/ui/ObjectId.tsx +3 -4
- package/ui/Pagination.tsx +3 -4
- package/ui/Popconfirm.tsx +5 -7
- package/ui/Select.tsx +1 -1
- package/ui/System/CSR.tsx +6 -2
- package/ui/ToggleSelect.tsx +4 -3
- package/ui/UiOverride/context.ts +12 -2
- package/ui/recipe/badgeRecipe.ts +22 -3
- package/ui/recipe/buttonRecipe.ts +51 -2
- package/ui/recipe/factory.ts +2 -2
- package/ui/recipe/inputRecipe.ts +18 -4
- package/webkit/useCsrValues.ts +121 -4
- package/webkit/useFrameRuntime.ts +42 -12
- package/server/resolver/CascadeRunner.ts +0 -201
- package/types/server/resolver/CascadeRunner.d.ts +0 -13
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PromiseOrObject } from "akanjs/base";
|
|
2
2
|
import { capitalize } from "akanjs/common";
|
|
3
|
-
import type
|
|
3
|
+
import { type ConstantModel, ConstantRegistry, type QueryOf } from "akanjs/constant";
|
|
4
4
|
import {
|
|
5
5
|
type CRUDEventType,
|
|
6
6
|
type DatabaseModel,
|
|
@@ -15,10 +15,13 @@ import {
|
|
|
15
15
|
type SaveEventType,
|
|
16
16
|
} from "akanjs/document";
|
|
17
17
|
import type { DatabaseService, ServiceCls } from "akanjs/service";
|
|
18
|
-
import type { CascadeRunner } from "./CascadeRunner";
|
|
19
18
|
|
|
20
19
|
export class ServiceResolver {
|
|
21
|
-
static #getDefaultDbServiceMethods(
|
|
20
|
+
static #getDefaultDbServiceMethods(
|
|
21
|
+
className: string,
|
|
22
|
+
cascades: [string, string][],
|
|
23
|
+
getService: (refName: string) => DatabaseService,
|
|
24
|
+
) {
|
|
22
25
|
const dbServiceMethods = {
|
|
23
26
|
async __get(this: DatabaseService, id: string) {
|
|
24
27
|
return await this.__databaseModel.__get(id);
|
|
@@ -96,24 +99,37 @@ export class ServiceResolver {
|
|
|
96
99
|
return this.__update(id, data);
|
|
97
100
|
},
|
|
98
101
|
async __remove(this: DatabaseService, id: string): Promise<Doc> {
|
|
102
|
+
|
|
103
|
+
const targets = cascades.map(([key, refName]) => [key, getService(refName)] as const);
|
|
99
104
|
await this.__libsPreRemove(id);
|
|
100
105
|
const doc = await this.__databaseModel.__remove(id);
|
|
101
106
|
const removed = await this.__libsPostRemove(doc);
|
|
102
|
-
|
|
107
|
+
for (const [key, target] of targets) {
|
|
108
|
+
const value = (removed as Record<string, unknown>)[key];
|
|
109
|
+
const ids = (Array.isArray(value) ? value : [value]).filter((v): v is string => typeof v === "string");
|
|
110
|
+
|
|
111
|
+
for (const targetId of ids) await target.__remove(targetId);
|
|
112
|
+
}
|
|
103
113
|
return removed;
|
|
104
114
|
},
|
|
105
115
|
async [`remove${className}`](this: DatabaseService, id: string): Promise<Doc> {
|
|
106
116
|
return this.__remove(id);
|
|
107
117
|
},
|
|
108
|
-
async __removeMany(this: DatabaseService, query: QueryOf<any>) {
|
|
109
|
-
return await this.__databaseModel.__removeMany(query);
|
|
110
|
-
},
|
|
111
118
|
};
|
|
112
119
|
return dbServiceMethods;
|
|
113
120
|
}
|
|
114
|
-
static resolveDatabaseService(
|
|
121
|
+
static resolveDatabaseService(
|
|
122
|
+
constant: ConstantModel,
|
|
123
|
+
database: DatabaseModel,
|
|
124
|
+
srvRef: ServiceCls,
|
|
125
|
+
getService: (refName: string) => DatabaseService,
|
|
126
|
+
): ServiceCls {
|
|
115
127
|
const className = capitalize(database.refName);
|
|
116
|
-
|
|
128
|
+
|
|
129
|
+
const cascades = [...constant.full.cascade.remove].map(
|
|
130
|
+
([key, modelRef]) => [key, ConstantRegistry.getRefName(modelRef)] as [string, string],
|
|
131
|
+
);
|
|
132
|
+
Object.assign(srvRef.prototype, ServiceResolver.#getDefaultDbServiceMethods(className, cascades, getService));
|
|
117
133
|
const getQueryDataFromKey = (queryKey: string, args: any): { query: any; queryOption: any } => {
|
|
118
134
|
const lastArg = args.at(-1);
|
|
119
135
|
const hasQueryOption =
|
package/server/rscWorker.tsx
CHANGED
|
@@ -24,7 +24,6 @@ import { renderToReadableStream } from "react-server-dom-webpack/server.node";
|
|
|
24
24
|
import type { ClientManifest } from "./artifact";
|
|
25
25
|
import {
|
|
26
26
|
LruTtlCache,
|
|
27
|
-
type LruTtlCacheOptions,
|
|
28
27
|
parsePositiveInt,
|
|
29
28
|
type RouteCacheEntry,
|
|
30
29
|
type RouteCacheInvalidation,
|
|
@@ -123,9 +122,6 @@ interface FlightRenderResult {
|
|
|
123
122
|
cancelled: boolean;
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
/** Well above any sensible TTL: this reclaims a cache nobody is reading, not tracks expiry closely. */
|
|
127
|
-
const RESULT_CACHE_SWEEP_INTERVAL_MS = 60_000;
|
|
128
|
-
|
|
129
125
|
function hashRscTraceCacheKey(cacheKey: string): string {
|
|
130
126
|
let hash = 5381;
|
|
131
127
|
for (let index = 0; index < cacheKey.length; index += 1) hash = (hash * 33) ^ cacheKey.charCodeAt(index);
|
|
@@ -201,14 +197,11 @@ export class RscRenderer {
|
|
|
201
197
|
pagesBundleBuildId: 0,
|
|
202
198
|
};
|
|
203
199
|
readonly #routeStats = new Map<string, RouteRenderStats>();
|
|
204
|
-
|
|
205
200
|
#resultCache = new LruTtlCache<CachedRscResult>(
|
|
206
201
|
parsePositiveInt(process.env.AKAN_RSC_RESULT_CACHE_MAX_ENTRIES) ?? 100,
|
|
207
|
-
RscRenderer.#resultCacheOptions(),
|
|
208
202
|
);
|
|
209
203
|
#patchResultCache = new LruTtlCache<CachedRscResult>(
|
|
210
204
|
parsePositiveInt(process.env.AKAN_RSC_RESULT_CACHE_MAX_ENTRIES) ?? 100,
|
|
211
|
-
RscRenderer.#resultCacheOptions(),
|
|
212
205
|
);
|
|
213
206
|
readonly #activeRenderReaders = new Map<string, ReadableStreamDefaultReader<Uint8Array>>();
|
|
214
207
|
readonly #cancelledRenderRequests = new Set<string>();
|
|
@@ -825,11 +818,7 @@ export class RscRenderer {
|
|
|
825
818
|
rscLoadedRouteModuleKeys: routeStats.loadedModuleKeys,
|
|
826
819
|
rscTopRoutesByRenderCount: this.#topRoutes((route) => route.count),
|
|
827
820
|
rscTopRoutesByFlightBytes: this.#topRoutes((route) => route.flightBytes),
|
|
828
|
-
|
|
829
|
-
rscResultCacheEntries: this.#resultCache.size,
|
|
830
|
-
rscResultCacheBytes: this.#resultCache.byteSize,
|
|
831
|
-
rscPatchResultCacheEntries: this.#patchResultCache.size,
|
|
832
|
-
rscPatchResultCacheBytes: this.#patchResultCache.byteSize,
|
|
821
|
+
rscResultCacheEntries: this.#resultCache.size + this.#patchResultCache.size,
|
|
833
822
|
rscResultCacheHits: this.#resultCacheHits,
|
|
834
823
|
rscResultCacheMisses: this.#resultCacheMisses,
|
|
835
824
|
rscResultCacheBypass: this.#resultCacheBypass,
|
|
@@ -910,7 +899,7 @@ export class RscRenderer {
|
|
|
910
899
|
});
|
|
911
900
|
};
|
|
912
901
|
try {
|
|
913
|
-
|
|
902
|
+
while (true) {
|
|
914
903
|
if (options.requestId && this.#cancelledRenderRequests.has(options.requestId)) {
|
|
915
904
|
await reader.cancel();
|
|
916
905
|
return { chunks, bytes, chunksCount, control: null, lateControlSent, cancelled: true };
|
|
@@ -1136,15 +1125,6 @@ export class RscRenderer {
|
|
|
1136
1125
|
return decision;
|
|
1137
1126
|
}
|
|
1138
1127
|
|
|
1139
|
-
static #resultCacheOptions(): LruTtlCacheOptions<CachedRscResult> {
|
|
1140
|
-
return {
|
|
1141
|
-
sizeOf: (result) => result.bytes,
|
|
1142
|
-
maxBytes: LruTtlCache.parseByteCeiling(process.env.AKAN_RSC_RESULT_CACHE_MAX_BYTES),
|
|
1143
|
-
maxEntryBytes: LruTtlCache.parseByteCeiling(process.env.AKAN_RSC_RESULT_CACHE_MAX_BODY_BYTES),
|
|
1144
|
-
sweepIntervalMs: RESULT_CACHE_SWEEP_INTERVAL_MS,
|
|
1145
|
-
};
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
1128
|
#getCachedResult(cacheKey: string): CachedRscResult | null {
|
|
1149
1129
|
const cached = this.#resultCache.get(cacheKey);
|
|
1150
1130
|
if (!cached) {
|
|
@@ -1166,19 +1146,11 @@ export class RscRenderer {
|
|
|
1166
1146
|
}
|
|
1167
1147
|
|
|
1168
1148
|
#setCachedResult(cacheKey: string, result: CachedRscResult, ttl: number): void {
|
|
1169
|
-
this.#
|
|
1149
|
+
this.#resultCache.set(cacheKey, result, ttl);
|
|
1170
1150
|
}
|
|
1171
1151
|
|
|
1172
1152
|
#setCachedPatchResult(cacheKey: string, result: CachedRscResult, ttl: number): void {
|
|
1173
|
-
this.#
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
/** A silently dropped store looks identical to a cache miss; say which route is too big to cache. */
|
|
1177
|
-
#logRejectedStore(kind: string, result: CachedRscResult, stored: boolean): void {
|
|
1178
|
-
if (stored) return;
|
|
1179
|
-
this.#logger.verbose(
|
|
1180
|
-
`${kind} result cache store skipped pathname=${result.pathname} bytes=${result.bytes} reason=body-too-large`,
|
|
1181
|
-
);
|
|
1153
|
+
this.#patchResultCache.set(cacheKey, result, ttl);
|
|
1182
1154
|
}
|
|
1183
1155
|
|
|
1184
1156
|
#runWithRequest<T>(request: Request, fn: () => Promise<T>): Promise<T> {
|
package/server/rscWorkerHost.ts
CHANGED
|
@@ -89,66 +89,6 @@ export function createRscWorkerInvalidateCacheMessage(
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
/**
|
|
93
|
-
* The RSC worker samples its own process with the same `ProcessMetricsCollector.collect` its host
|
|
94
|
-
* replica uses, so its report carries `pid` / `rssBytes` / `role` / … under the identical names.
|
|
95
|
-
* `AkanServer` spreads the host's report into its own `collect({ role, ...webRouter.getMetrics() })`
|
|
96
|
-
* and `collect` spreads `extra` last (`processMetricsCollector.ts:122`) — so passing these through
|
|
97
|
-
* unprefixed silently overwrote the replica's own process fields, and the replica's RSS was reported
|
|
98
|
-
* nowhere. Rename them onto `rscWorker*`; the `rsc*` render counters, which only the worker
|
|
99
|
-
* produces, pass through untouched.
|
|
100
|
-
*
|
|
101
|
-
* XXX A new process-level field in `ProcessMetricsCollector.collect` must be added here too, or it
|
|
102
|
-
* starts shadowing the replica again.
|
|
103
|
-
*/
|
|
104
|
-
export function projectRscWorkerProcessMetrics(metrics: AkanMetricsReport): AkanMetricsReport {
|
|
105
|
-
const {
|
|
106
|
-
role: _role,
|
|
107
|
-
pid: _pid,
|
|
108
|
-
trace: _trace,
|
|
109
|
-
reportedAt,
|
|
110
|
-
rssBytes,
|
|
111
|
-
heapTotalBytes,
|
|
112
|
-
heapUsedBytes,
|
|
113
|
-
externalBytes,
|
|
114
|
-
arrayBuffersBytes,
|
|
115
|
-
cpuUserMicros,
|
|
116
|
-
cpuSystemMicros,
|
|
117
|
-
maxRssKb,
|
|
118
|
-
jscHeapSizeBytes,
|
|
119
|
-
jscHeapCapacityBytes,
|
|
120
|
-
jscExtraMemorySizeBytes,
|
|
121
|
-
jscObjectCount,
|
|
122
|
-
jscProtectedObjectCount,
|
|
123
|
-
eventLoopLagMeanMs,
|
|
124
|
-
eventLoopLagP99Ms,
|
|
125
|
-
eventLoopLagMaxMs,
|
|
126
|
-
gcDurationMs,
|
|
127
|
-
...renderMetrics
|
|
128
|
-
} = metrics;
|
|
129
|
-
return {
|
|
130
|
-
...renderMetrics,
|
|
131
|
-
rscWorkerReportedAt: reportedAt,
|
|
132
|
-
rscWorkerRssBytes: rssBytes,
|
|
133
|
-
rscWorkerHeapTotalBytes: heapTotalBytes,
|
|
134
|
-
rscWorkerHeapUsedBytes: heapUsedBytes,
|
|
135
|
-
rscWorkerExternalBytes: externalBytes,
|
|
136
|
-
rscWorkerArrayBuffersBytes: arrayBuffersBytes,
|
|
137
|
-
rscWorkerCpuUserMicros: cpuUserMicros,
|
|
138
|
-
rscWorkerCpuSystemMicros: cpuSystemMicros,
|
|
139
|
-
rscWorkerMaxRssKb: maxRssKb,
|
|
140
|
-
rscWorkerJscHeapSizeBytes: jscHeapSizeBytes,
|
|
141
|
-
rscWorkerJscHeapCapacityBytes: jscHeapCapacityBytes,
|
|
142
|
-
rscWorkerJscExtraMemorySizeBytes: jscExtraMemorySizeBytes,
|
|
143
|
-
rscWorkerJscObjectCount: jscObjectCount,
|
|
144
|
-
rscWorkerJscProtectedObjectCount: jscProtectedObjectCount,
|
|
145
|
-
rscWorkerEventLoopLagMeanMs: eventLoopLagMeanMs,
|
|
146
|
-
rscWorkerEventLoopLagP99Ms: eventLoopLagP99Ms,
|
|
147
|
-
rscWorkerEventLoopLagMaxMs: eventLoopLagMaxMs,
|
|
148
|
-
rscWorkerGcDurationMs: gcDurationMs,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
92
|
export function createRscHostRenderStream(input: {
|
|
153
93
|
setPending: (pending: RscPending) => void;
|
|
154
94
|
deletePending: () => void;
|
|
@@ -524,7 +464,18 @@ export class RscWorker {
|
|
|
524
464
|
}
|
|
525
465
|
|
|
526
466
|
getMetrics(): AkanMetricsReport {
|
|
527
|
-
|
|
467
|
+
const {
|
|
468
|
+
rscWorkerPid: _rscWorkerPid,
|
|
469
|
+
rscWorkerStatus: _rscWorkerStatus,
|
|
470
|
+
rscWorkerRestartCount: _rscWorkerRestartCount,
|
|
471
|
+
rscWorkerRecycleCount: _rscWorkerRecycleCount,
|
|
472
|
+
rscWorkerLastRecycleReason: _rscWorkerLastRecycleReason,
|
|
473
|
+
rscPendingRenderCount: _rscPendingRenderCount,
|
|
474
|
+
rscQueuedSendCount: _rscQueuedSendCount,
|
|
475
|
+
rscHostPendingChunkOverflowCount: _rscHostPendingChunkOverflowCount,
|
|
476
|
+
...workerMetrics
|
|
477
|
+
} = this.#lastWorkerMetrics;
|
|
478
|
+
return Object.assign(workerMetrics, {
|
|
528
479
|
rscWorkerPid: this.#proc.pid,
|
|
529
480
|
rscWorkerStatus: this.#status,
|
|
530
481
|
rscWorkerRestartCount: this.#restartCount,
|
|
@@ -411,7 +411,7 @@ export function interleaveRscScriptsWithHtml(
|
|
|
411
411
|
const pumpRscScripts = async () => {
|
|
412
412
|
rscReader = rscClientStream.getReader();
|
|
413
413
|
try {
|
|
414
|
-
|
|
414
|
+
while (true) {
|
|
415
415
|
const { value, done } = await rscReader.read();
|
|
416
416
|
if (done || errored) break;
|
|
417
417
|
await waitForRscQueueDrain();
|
|
@@ -446,7 +446,7 @@ export function interleaveRscScriptsWithHtml(
|
|
|
446
446
|
|
|
447
447
|
htmlReader = htmlStream.getReader();
|
|
448
448
|
try {
|
|
449
|
-
|
|
449
|
+
while (true) {
|
|
450
450
|
const { value, done } = await htmlReader.read();
|
|
451
451
|
if (done || errored) break;
|
|
452
452
|
controller.enqueue(value);
|
package/server/webRouter.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
3
4
|
import { getEnv } from "akanjs/base";
|
|
4
5
|
import {
|
|
5
6
|
type AkanI18nConfig,
|
|
@@ -56,10 +57,20 @@ import type { BaseBuildArtifact, HttpRoutes, RenderState } from "./types";
|
|
|
56
57
|
|
|
57
58
|
const CLIENT_CLOSED_REQUEST_STATUS = 499;
|
|
58
59
|
export const DEFAULT_HTML_RESULT_CACHE_MAX_BODY_BYTES = 2 * 1024 * 1024;
|
|
59
|
-
/** Well above any sensible TTL: this only has to reclaim a cache nobody is reading, not track expiry closely. */
|
|
60
|
-
const ROUTE_CACHE_SWEEP_INTERVAL_MS = 60_000;
|
|
61
60
|
const APPLE_APP_SITE_ASSOCIATION_PATH = "/.well-known/apple-app-site-association";
|
|
62
61
|
const ANDROID_ASSET_LINKS_PATH = "/.well-known/assetlinks.json";
|
|
62
|
+
const FIREBASE_MESSAGING_SW_PATH = "/firebase-messaging-sw.js";
|
|
63
|
+
const FIREBASE_WEB_SDK_VERSION = "12.13.0";
|
|
64
|
+
|
|
65
|
+
interface FirebaseClientEnvConfig {
|
|
66
|
+
apiKey: string;
|
|
67
|
+
authDomain?: string;
|
|
68
|
+
projectId: string;
|
|
69
|
+
storageBucket?: string;
|
|
70
|
+
messagingSenderId: string;
|
|
71
|
+
appId: string;
|
|
72
|
+
vapidKey?: string;
|
|
73
|
+
}
|
|
63
74
|
|
|
64
75
|
export function createRscRedirectResponse(
|
|
65
76
|
location: string,
|
|
@@ -288,11 +299,6 @@ export class WebRouter {
|
|
|
288
299
|
};
|
|
289
300
|
readonly #htmlCache = new LruTtlCache<CachedHtmlResult>(
|
|
290
301
|
parsePositiveInt(process.env.AKAN_HTML_RESULT_CACHE_MAX_ENTRIES) ?? 100,
|
|
291
|
-
{
|
|
292
|
-
sizeOf: (result) => result.html.length,
|
|
293
|
-
maxBytes: LruTtlCache.parseByteCeiling(process.env.AKAN_HTML_RESULT_CACHE_MAX_BYTES),
|
|
294
|
-
sweepIntervalMs: ROUTE_CACHE_SWEEP_INTERVAL_MS,
|
|
295
|
-
},
|
|
296
302
|
);
|
|
297
303
|
#htmlCacheHits = 0;
|
|
298
304
|
#htmlCacheMisses = 0;
|
|
@@ -486,6 +492,16 @@ export class WebRouter {
|
|
|
486
492
|
WebRouter.#deepLinkAssociationResponse(ANDROID_ASSET_LINKS_PATH, this.#artifact, {
|
|
487
493
|
cacheControl: this.#prodMode ? "public, max-age=3600" : "no-store",
|
|
488
494
|
}) ?? new Response("Not Found", { status: 404 }),
|
|
495
|
+
[FIREBASE_MESSAGING_SW_PATH]: async () => {
|
|
496
|
+
this.#requestStats.staticAsset += 1;
|
|
497
|
+
const firebaseConfig = await WebRouter.#resolveFirebaseClientConfig();
|
|
498
|
+
return new Response(WebRouter.#createFirebaseMessagingServiceWorker(firebaseConfig), {
|
|
499
|
+
headers: {
|
|
500
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
501
|
+
"Cache-Control": "no-store",
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
},
|
|
489
505
|
"/*": async (req) => {
|
|
490
506
|
const url = new URL(req.url);
|
|
491
507
|
if (WebRouter.#isImageOptimizerPath(url.pathname)) {
|
|
@@ -676,7 +692,6 @@ export class WebRouter {
|
|
|
676
692
|
this.#devHmr?.dispose();
|
|
677
693
|
this.#devHmr = null;
|
|
678
694
|
this.#builderRpc = null;
|
|
679
|
-
this.#htmlCache.dispose();
|
|
680
695
|
this.#rsc.kill();
|
|
681
696
|
this.#hub = null;
|
|
682
697
|
}
|
|
@@ -694,7 +709,6 @@ export class WebRouter {
|
|
|
694
709
|
httpCsrCount: this.#requestStats.csr,
|
|
695
710
|
httpImageCount: this.#requestStats.image,
|
|
696
711
|
httpHtmlCacheEntries: this.#htmlCache.size,
|
|
697
|
-
httpHtmlCacheBytes: this.#htmlCache.byteSize,
|
|
698
712
|
httpHtmlCacheHits: this.#htmlCacheHits,
|
|
699
713
|
httpHtmlCacheMisses: this.#htmlCacheMisses,
|
|
700
714
|
httpHtmlCacheBypass: this.#htmlCacheBypass,
|
|
@@ -994,6 +1008,79 @@ export class WebRouter {
|
|
|
994
1008
|
return process.env.AKAN_APP_DIR ?? path.dirname(Bun.main);
|
|
995
1009
|
}
|
|
996
1010
|
|
|
1011
|
+
static async #resolveFirebaseClientConfig(): Promise<FirebaseClientEnvConfig | null> {
|
|
1012
|
+
const envPath = path.join(WebRouter.#resolveAppDir(), "env", "env.client.ts");
|
|
1013
|
+
if (!fs.existsSync(envPath)) return null;
|
|
1014
|
+
try {
|
|
1015
|
+
const envUrl = pathToFileURL(envPath);
|
|
1016
|
+
envUrl.searchParams.set("t", String(Date.now()));
|
|
1017
|
+
const envModule = (await import(envUrl.href)) as { env?: { firebase?: unknown } };
|
|
1018
|
+
return WebRouter.#normalizeFirebaseClientConfig(envModule.env?.firebase);
|
|
1019
|
+
} catch {
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
static #normalizeFirebaseClientConfig(config: unknown): FirebaseClientEnvConfig | null {
|
|
1025
|
+
if (!config || typeof config !== "object") return null;
|
|
1026
|
+
const value = config as Partial<Record<keyof FirebaseClientEnvConfig, unknown>>;
|
|
1027
|
+
if (
|
|
1028
|
+
typeof value.apiKey !== "string" ||
|
|
1029
|
+
typeof value.projectId !== "string" ||
|
|
1030
|
+
typeof value.messagingSenderId !== "string" ||
|
|
1031
|
+
typeof value.appId !== "string"
|
|
1032
|
+
) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
apiKey: value.apiKey,
|
|
1037
|
+
...(typeof value.authDomain === "string" ? { authDomain: value.authDomain } : {}),
|
|
1038
|
+
projectId: value.projectId,
|
|
1039
|
+
...(typeof value.storageBucket === "string" ? { storageBucket: value.storageBucket } : {}),
|
|
1040
|
+
messagingSenderId: value.messagingSenderId,
|
|
1041
|
+
appId: value.appId,
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
static #createFirebaseMessagingServiceWorker(config: FirebaseClientEnvConfig | null): string {
|
|
1046
|
+
const configJson = JSON.stringify(config);
|
|
1047
|
+
return `/* Generated by Akan.js. Do not edit. */
|
|
1048
|
+
const firebaseConfig = ${configJson};
|
|
1049
|
+
|
|
1050
|
+
if (firebaseConfig) {
|
|
1051
|
+
importScripts("https://www.gstatic.com/firebasejs/${FIREBASE_WEB_SDK_VERSION}/firebase-app-compat.js");
|
|
1052
|
+
importScripts("https://www.gstatic.com/firebasejs/${FIREBASE_WEB_SDK_VERSION}/firebase-messaging-compat.js");
|
|
1053
|
+
|
|
1054
|
+
firebase.initializeApp(firebaseConfig);
|
|
1055
|
+
const messaging = firebase.messaging();
|
|
1056
|
+
|
|
1057
|
+
const notificationUrl = (payload) =>
|
|
1058
|
+
payload?.data?.url || payload?.fcmOptions?.link || payload?.notification?.click_action;
|
|
1059
|
+
|
|
1060
|
+
messaging.onBackgroundMessage((payload) => {
|
|
1061
|
+
const title = payload?.notification?.title || "";
|
|
1062
|
+
const options = {
|
|
1063
|
+
body: payload?.notification?.body,
|
|
1064
|
+
icon: payload?.notification?.icon,
|
|
1065
|
+
image: payload?.notification?.image,
|
|
1066
|
+
data: {
|
|
1067
|
+
url: notificationUrl(payload),
|
|
1068
|
+
FCM_MSG: payload,
|
|
1069
|
+
},
|
|
1070
|
+
};
|
|
1071
|
+
self.registration.showNotification(title, options);
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
self.addEventListener("notificationclick", (event) => {
|
|
1076
|
+
const url = event.notification?.data?.url || event.notification?.data?.FCM_MSG?.data?.url;
|
|
1077
|
+
event.notification?.close();
|
|
1078
|
+
if (!url) return;
|
|
1079
|
+
event.waitUntil(clients.openWindow(url));
|
|
1080
|
+
});
|
|
1081
|
+
`;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
997
1084
|
static #normalizeArtifact(artifact: BaseBuildArtifact, artifactDir: string): BaseBuildArtifact {
|
|
998
1085
|
const normalizedArtifactDir = path.resolve(artifactDir);
|
|
999
1086
|
const pagesBundlePath = WebRouter.#resolveArtifactPath(artifact.pagesBundlePath, normalizedArtifactDir);
|
package/service/ipcTypes.ts
CHANGED
|
@@ -55,26 +55,6 @@ export interface AkanMetricsReport {
|
|
|
55
55
|
rscWorkerRestartCount?: number;
|
|
56
56
|
rscWorkerRecycleCount?: number;
|
|
57
57
|
rscWorkerLastRecycleReason?: string;
|
|
58
|
-
|
|
59
|
-
rscWorkerReportedAt?: number;
|
|
60
|
-
rscWorkerRssBytes?: number;
|
|
61
|
-
rscWorkerHeapTotalBytes?: number;
|
|
62
|
-
rscWorkerHeapUsedBytes?: number;
|
|
63
|
-
rscWorkerExternalBytes?: number;
|
|
64
|
-
rscWorkerArrayBuffersBytes?: number;
|
|
65
|
-
rscWorkerCpuUserMicros?: number;
|
|
66
|
-
rscWorkerCpuSystemMicros?: number;
|
|
67
|
-
rscWorkerMaxRssKb?: number;
|
|
68
|
-
rscWorkerJscHeapSizeBytes?: number;
|
|
69
|
-
rscWorkerJscHeapCapacityBytes?: number;
|
|
70
|
-
/** Off-heap bytes JSC attributes to JS objects — typed-array backing stores, i.e. cached Flight chunks. */
|
|
71
|
-
rscWorkerJscExtraMemorySizeBytes?: number;
|
|
72
|
-
rscWorkerJscObjectCount?: number;
|
|
73
|
-
rscWorkerJscProtectedObjectCount?: number;
|
|
74
|
-
rscWorkerEventLoopLagMeanMs?: number;
|
|
75
|
-
rscWorkerEventLoopLagP99Ms?: number;
|
|
76
|
-
rscWorkerEventLoopLagMaxMs?: number;
|
|
77
|
-
rscWorkerGcDurationMs?: number;
|
|
78
58
|
rscPendingRenderCount?: number;
|
|
79
59
|
rscQueuedSendCount?: number;
|
|
80
60
|
rscHostPendingChunkOverflowCount?: number;
|
|
@@ -100,13 +80,9 @@ export interface AkanMetricsReport {
|
|
|
100
80
|
rscLastRenderLoadedModuleDelta?: number;
|
|
101
81
|
rscLastRenderLoadedModules?: string[];
|
|
102
82
|
rscResultCacheEntries?: number;
|
|
103
|
-
rscResultCacheBytes?: number;
|
|
104
|
-
rscPatchResultCacheEntries?: number;
|
|
105
|
-
rscPatchResultCacheBytes?: number;
|
|
106
83
|
rscResultCacheHits?: number;
|
|
107
84
|
rscResultCacheMisses?: number;
|
|
108
85
|
rscResultCacheBypass?: number;
|
|
109
|
-
/** Keys the SSR chunk registry tracks — NOT a memory bound; the modules stay in Bun's ESM registry. */
|
|
110
86
|
ssrChunkRegistrySize?: number;
|
|
111
87
|
ssrChunkLoadCount?: number;
|
|
112
88
|
ssrChunkCacheHitCount?: number;
|
|
@@ -117,7 +93,6 @@ export interface AkanMetricsReport {
|
|
|
117
93
|
httpCsrCount?: number;
|
|
118
94
|
httpImageCount?: number;
|
|
119
95
|
httpHtmlCacheEntries?: number;
|
|
120
|
-
httpHtmlCacheBytes?: number;
|
|
121
96
|
httpHtmlCacheHits?: number;
|
|
122
97
|
httpHtmlCacheMisses?: number;
|
|
123
98
|
httpHtmlCacheBypass?: number;
|
|
@@ -95,7 +95,7 @@ export interface DocumentStore {
|
|
|
95
95
|
query: DocumentQuery,
|
|
96
96
|
update: DocumentUpdateInput,
|
|
97
97
|
): Promise<{ acknowledged: boolean; matchedCount: number; modifiedCount: number }>;
|
|
98
|
-
|
|
98
|
+
deleteManyByQuery(
|
|
99
99
|
query: DocumentQuery,
|
|
100
100
|
): Promise<{ acknowledged: boolean; matchedCount: number; modifiedCount: number }>;
|
|
101
101
|
bulkWrite(
|
|
@@ -1081,8 +1081,8 @@ export class SqlDocumentStore {
|
|
|
1081
1081
|
return { acknowledged: true, matchedCount: changes, modifiedCount: changes };
|
|
1082
1082
|
}
|
|
1083
1083
|
|
|
1084
|
-
async
|
|
1085
|
-
|
|
1084
|
+
async deleteManyByQuery(query: DocumentQuery) {
|
|
1085
|
+
|
|
1086
1086
|
return this.updateManyByQuery(query, { removedAt: dayjs() });
|
|
1087
1087
|
}
|
|
1088
1088
|
|
package/service/serve.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Cls, INJECT_META
|
|
1
|
+
import { type Cls, INJECT_META } from "akanjs/base";
|
|
2
2
|
import { applyMixins, capitalize, Logger, lowerlize } from "akanjs/common";
|
|
3
3
|
import type { DatabaseModel } from "akanjs/document";
|
|
4
4
|
|
|
@@ -146,10 +146,6 @@ export function serve(
|
|
|
146
146
|
const postUpdateFns = extSrvs.map((srv) => srv.prototype._postUpdate);
|
|
147
147
|
const preRemoveFns = extSrvs.map((srv) => srv.prototype._preRemove);
|
|
148
148
|
const postRemoveFns = extSrvs.map((srv) => srv.prototype._postRemove);
|
|
149
|
-
|
|
150
|
-
Object.assign(srvRef, {
|
|
151
|
-
[LIBS_REMOVE_HOOK]: preRemoveFns.some(Boolean) || postRemoveFns.some(Boolean),
|
|
152
|
-
});
|
|
153
149
|
Object.assign(srvRef.prototype, {
|
|
154
150
|
async __libsPreCreate(this: DatabaseService, data: DatabaseServiceData) {
|
|
155
151
|
let result = data;
|
package/service/types.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
ListQueryOption,
|
|
12
12
|
QueryMethodPart,
|
|
13
13
|
SaveEventType,
|
|
14
|
-
UpdateResult,
|
|
15
14
|
} from "akanjs/document";
|
|
16
15
|
|
|
17
16
|
type ServiceMixinOmitKey =
|
|
@@ -102,7 +101,6 @@ export type DatabaseService<
|
|
|
102
101
|
__create: (data: _DataInputOfDoc) => Promise<Doc>;
|
|
103
102
|
__update: (id: string, data: Partial<Doc>) => Promise<Doc>;
|
|
104
103
|
__remove: (id: string) => Promise<Doc>;
|
|
105
|
-
__removeMany: (query: _QueryOfDoc) => Promise<UpdateResult>;
|
|
106
104
|
__list(query?: _QueryOfDoc, queryOption?: _ListQueryOption): Promise<Doc[]>;
|
|
107
105
|
__listIds(query?: _QueryOfDoc, queryOption?: _ListQueryOption): Promise<string[]>;
|
|
108
106
|
__find(query?: _QueryOfDoc, queryOption?: _FindQueryOption): Promise<Doc | null>;
|
package/types/base/symbols.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export declare const SLICE_META: unique symbol;
|
|
|
3
3
|
export declare const FILTER_META: unique symbol;
|
|
4
4
|
export declare const LOADER_META: unique symbol;
|
|
5
5
|
export declare const INJECT_META: unique symbol;
|
|
6
|
-
export declare const LIBS_REMOVE_HOOK: unique symbol;
|
|
7
6
|
export declare const ENDPOINT_META: unique symbol;
|
|
8
7
|
export declare const ENDPOINT_DICT_SHAPE: unique symbol;
|
|
9
8
|
export declare const SLICE_DICT_SHAPE: unique symbol;
|
package/types/client/cn.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ import { type ClassNameValue } from "tailwind-merge";
|
|
|
6
6
|
* `cn("bg-primary", "bg-open")` correctly resolves to `"bg-open"` instead of keeping both.
|
|
7
7
|
*/
|
|
8
8
|
export declare const colorTokens: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Akan's semantic radius tokens (`--radius-box` / `--radius-field` in ui/styles.css). Without them
|
|
11
|
+
* `cn("rounded-field", "rounded-full")` keeps both classes and stylesheet order decides the winner.
|
|
12
|
+
*/
|
|
13
|
+
export declare const radiusTokens: string[];
|
|
9
14
|
/** The one class-combining function: joins strings/arrays/conditional parts (`cond && "x"`) and
|
|
10
15
|
* resolves Tailwind conflicts with a shared tailwind-merge instance that knows Akan's semantic
|
|
11
16
|
* color tokens. clsx-style object syntax (`{ x: cond }`) is not supported — write `cond && "x"`. */
|
|
@@ -428,6 +428,7 @@ export interface FrameLayoutState {
|
|
|
428
428
|
keyboard: KeyboardFrameState;
|
|
429
429
|
contentViewport: FrameContentViewportState;
|
|
430
430
|
keyboardAccessory: KeyboardAccessoryFrameState;
|
|
431
|
+
contentAnchor?: "bottom";
|
|
431
432
|
platformProfile: FramePlatformProfile;
|
|
432
433
|
zIndex: FrameLayerZIndex;
|
|
433
434
|
pageStateByPath: Map<string, PageState>;
|
|
@@ -436,6 +437,7 @@ export interface FrameSlotRegistration {
|
|
|
436
437
|
scope?: FrameSlotScope;
|
|
437
438
|
type: FrameSlotType;
|
|
438
439
|
role?: FrameSlotRole;
|
|
440
|
+
contentAnchor?: "bottom";
|
|
439
441
|
height?: number;
|
|
440
442
|
estimatedHeight?: number;
|
|
441
443
|
source?: "navbar" | "topInset" | "bottomInset" | "bottomTab" | (string & {});
|
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import type { FieldObject } from "./fieldInfo.d.ts";
|
|
2
2
|
import type { ConstantModelRef } from "./via.d.ts";
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
* document is removed; `removeWith` removes this document when what the field points at is removed. The two
|
|
6
|
-
* read identically on a relation field, so the value has to name the direction — a mistake here is a data loss.
|
|
7
|
-
*/
|
|
8
|
-
export declare const cascadeActions: readonly ["removeRef", "removeWith"];
|
|
3
|
+
/** What happens to the documents a relation field points at when the owner is removed. */
|
|
4
|
+
export declare const cascadeActions: readonly ["remove"];
|
|
9
5
|
export type CascadeAction = (typeof cascadeActions)[number];
|
|
10
|
-
/** How a `removeWith` field names the owner whose removal takes this document with it. */
|
|
11
|
-
export interface CascadeWithPath {
|
|
12
|
-
readonly key: string;
|
|
13
|
-
/** Set when the field is a relation. Resolved to a refName later: the owner may not be registered yet. */
|
|
14
|
-
readonly modelRef: ConstantModelRef | null;
|
|
15
|
-
/** Set when the field declares `ref`, which names the owner at declaration. */
|
|
16
|
-
readonly refName: string | null;
|
|
17
|
-
/** Set when the field declares `refPath`: the sibling field holding the owner's refName. */
|
|
18
|
-
readonly typeKey: string | null;
|
|
19
|
-
/** The refNames `typeKey` may hold. Empty unless the field is polymorphic. */
|
|
20
|
-
readonly typeValues: readonly string[];
|
|
21
|
-
}
|
|
22
6
|
export declare class CascadePaths {
|
|
23
7
|
#private;
|
|
24
|
-
/** Field key → the model its ids point at
|
|
25
|
-
readonly
|
|
26
|
-
/** Field key → the owner whose removal removes this document. */
|
|
27
|
-
readonly removeWith: Map<string, CascadeWithPath>;
|
|
8
|
+
/** Field key → the model its ids point at. Resolved to a refName later: the target may not be registered yet. */
|
|
9
|
+
readonly remove: Map<string, ConstantModelRef>;
|
|
28
10
|
collect(fieldMap: FieldObject): this;
|
|
29
11
|
}
|
|
@@ -4,7 +4,7 @@ import type { DocumentModel, QueryOf } from "akanjs/constant";
|
|
|
4
4
|
import type { CacheAdaptor, CacheSetOptions } from "akanjs/service";
|
|
5
5
|
import type { DataLoader } from "./dataLoader.d.ts";
|
|
6
6
|
import type { ExtractQuery, ExtractSort, FilterInstance } from "./filterMeta.d.ts";
|
|
7
|
-
import type { CRUDEventType, Mdl, SaveEventType
|
|
7
|
+
import type { CRUDEventType, Mdl, SaveEventType } from "./into.d.ts";
|
|
8
8
|
import type { DataInputOf, FindQueryOption, ListQueryOption } from "./types.d.ts";
|
|
9
9
|
export declare class CacheDatabase<T = unknown> {
|
|
10
10
|
private readonly refName;
|
|
@@ -57,7 +57,6 @@ type DatabaseModelWithQuerySort<T extends string, Input, Doc, Obj, Insight, Quer
|
|
|
57
57
|
__create: (data: _DataInput) => Promise<Doc>;
|
|
58
58
|
__update: (id: string, data: Partial<Doc>) => Promise<Doc>;
|
|
59
59
|
__remove: (id: string) => Promise<Doc>;
|
|
60
|
-
__removeMany: (query: _QueryOfDoc) => Promise<UpdateResult>;
|
|
61
60
|
__list(query: _QueryOfDoc, queryOption?: _ListQueryOption): Promise<Doc[]>;
|
|
62
61
|
__listIds(query: _QueryOfDoc, queryOption?: _ListQueryOption): Promise<string[]>;
|
|
63
62
|
__find(query: _QueryOfDoc, queryOption?: _FindQueryOption): Promise<Doc | null>;
|
|
@@ -8,7 +8,7 @@ export interface DatabaseModel<T extends string = string, Input = any, Doc = any
|
|
|
8
8
|
model: ModelCls<Model>;
|
|
9
9
|
filter: FilterCls<Filter>;
|
|
10
10
|
obj: ConstantCls<Obj>;
|
|
11
|
-
insight:
|
|
11
|
+
insight: ConstantCls<Insight>;
|
|
12
12
|
_Input: Input;
|
|
13
13
|
_Doc: Doc;
|
|
14
14
|
_Model: Model;
|
|
@@ -32,6 +32,6 @@ export declare class DatabaseRegistry {
|
|
|
32
32
|
static getScalar<AllowEmpty extends boolean = false>(refName: string, { allowEmpty }?: {
|
|
33
33
|
allowEmpty?: AllowEmpty;
|
|
34
34
|
}): AllowEmpty extends true ? DatabaseCls | undefined : DatabaseCls;
|
|
35
|
-
static buildModel<T extends string, Input, Doc, Model, Obj, Insight, Filter extends FilterInstance, _Query extends ExtractQuery<Filter> = ExtractQuery<Filter>, _Sort extends ExtractSort<Filter> = ExtractSort<Filter>>(refName: T, input: DatabaseCls<Input>, doc: DatabaseCls<Doc>, model: ModelCls<Model>, obj: ConstantCls<Obj>, insight:
|
|
35
|
+
static buildModel<T extends string, Input, Doc, Model, Obj, Insight, Filter extends FilterInstance, _Query extends ExtractQuery<Filter> = ExtractQuery<Filter>, _Sort extends ExtractSort<Filter> = ExtractSort<Filter>>(refName: T, input: DatabaseCls<Input>, doc: DatabaseCls<Doc>, model: ModelCls<Model>, obj: ConstantCls<Obj>, insight: ConstantCls<Insight>, filter: FilterCls<Filter>): DatabaseModel<T, Input, Doc, Model, Obj, Insight, Filter, _Query, _Sort>;
|
|
36
36
|
static buildScalar<T extends string, Model>(refName: T, Model: DatabaseCls<Model>): DatabaseCls<Model>;
|
|
37
37
|
}
|
package/types/document/into.d.ts
CHANGED
|
@@ -60,17 +60,16 @@ export type Mdl<Doc, Raw, _RawDoc = DocumentModel<Raw>, _RawQuery extends Docume
|
|
|
60
60
|
exists(query: _RawQuery): Promise<string | null>;
|
|
61
61
|
updateOne(query: _RawQuery, update: DocumentUpdateInput<_RawDoc>, options?: DocumentUpdateOptions): Promise<UpdateResult>;
|
|
62
62
|
updateMany(query: _RawQuery, update: DocumentUpdateInput<_RawDoc>): Promise<UpdateResult>;
|
|
63
|
-
|
|
63
|
+
deleteMany(query: _RawQuery): Promise<UpdateResult>;
|
|
64
64
|
bulkWrite(operations: BulkWriteOperation<Raw, _RawDoc, _RawQuery>[]): Promise<UpdateResult>;
|
|
65
65
|
};
|
|
66
|
-
interface IntoConstantModel<T extends string, _CapitalizedRefName extends string, Raw
|
|
66
|
+
interface IntoConstantModel<T extends string, _CapitalizedRefName extends string, Raw> {
|
|
67
67
|
refName: T;
|
|
68
68
|
_CapitalizedRefName: _CapitalizedRefName;
|
|
69
69
|
_Full: Raw;
|
|
70
|
-
_Insight: Insight;
|
|
71
70
|
}
|
|
72
71
|
type NoInferType<T> = [T][T extends unknown ? 0 : never];
|
|
73
|
-
type IntoModelActions<T extends string, _CapitalizedRefName extends string, Doc, Raw,
|
|
72
|
+
type IntoModelActions<T extends string, _CapitalizedRefName extends string, Doc, Raw, _Query, _Sort, _QueryOfDoc = QueryOf<Doc>> = {
|
|
74
73
|
[key in _CapitalizedRefName]: Mdl<Doc, Raw>;
|
|
75
74
|
} & {
|
|
76
75
|
[key in `${Uncapitalize<_CapitalizedRefName>}Loader`]: DataLoader<string, Doc, string>;
|
|
@@ -88,6 +87,6 @@ type IntoModelActions<T extends string, _CapitalizedRefName extends string, Doc,
|
|
|
88
87
|
[K in `update${_CapitalizedRefName}`]: (id: string, data: Partial<Doc>) => Promise<Doc>;
|
|
89
88
|
} & {
|
|
90
89
|
[K in `remove${_CapitalizedRefName}`]: (id: string) => Promise<Doc>;
|
|
91
|
-
} & QueryMethodPart<_Query, _Sort, Raw, Doc,
|
|
92
|
-
export declare const into: <Doc, FilterRef extends FilterCls, T extends string, Raw,
|
|
90
|
+
} & QueryMethodPart<_Query, _Sort, Raw, Doc, unknown, unknown, unknown, _QueryOfDoc>;
|
|
91
|
+
export declare const into: <Doc, FilterRef extends FilterCls, T extends string, Raw, AddDbModels extends ModelCls[], _CapitalizedRefName extends string, _QueryOfDoc = QueryOf<Doc>, _Query = FilterQueryOf<FilterRef>, _Sort = FilterSortOf<FilterRef>, _LoaderBuilder extends LoaderBuilder<NoInferType<Doc>> = LoaderBuilder<Doc>>(docRef: Cls<Doc>, filterRef: FilterRef, cnst: IntoConstantModel<T, _CapitalizedRefName, Raw>, loaderBuilder: _LoaderBuilder, ...addMdls: [...AddDbModels]) => ModelCls<IntoModelActions<T, _CapitalizedRefName, Doc, Raw, _Query, _Sort, _QueryOfDoc>, ReturnType<_LoaderBuilder>>;
|
|
93
92
|
export {};
|