akanjs 2.0.5 → 2.0.7
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/README.ko.md +1 -1
- package/README.md +1 -1
- package/cli/application/application.command.ts +4 -1
- package/cli/application/application.runner.ts +6 -8
- package/cli/build.ts +3 -1
- package/cli/cloud/cloud.runner.ts +7 -8
- package/cli/index.js +288 -115
- package/cli/library/library.runner.ts +2 -2
- package/cli/module/module.runner.ts +2 -2
- package/cli/npmRegistry.ts +13 -0
- package/cli/openBrowser.ts +15 -0
- package/cli/pluralizeName.ts +5 -0
- package/cli/scalar/scalar.prompt.ts +2 -2
- package/cli/scalar/scalar.runner.ts +2 -2
- package/cli/semver.ts +18 -0
- package/cli/templates/lib/sig.ts +2 -2
- package/cli/workspace/workspace.runner.ts +3 -3
- package/client/cookie.ts +10 -15
- package/common/index.ts +1 -0
- package/common/jwtDecode.ts +17 -0
- package/constant/serialize.ts +1 -1
- package/devkit/akanApp/akanApp.host.ts +46 -9
- package/devkit/akanConfig/akanConfig.ts +2 -1
- package/devkit/capacitor.base.config.ts +18 -4
- package/devkit/capacitorApp.ts +118 -64
- package/devkit/incrementalBuilder/incrementalBuilder.host.ts +83 -9
- package/devkit/mobile/mobileTarget.ts +2 -1
- package/devkit/scanInfo.ts +1 -0
- package/document/dataLoader.ts +140 -6
- package/document/database.ts +1 -1
- package/package.json +7 -13
- package/server/akanApp.ts +250 -44
- package/server/di/diLifecycle.ts +1 -1
- package/server/processMetricsCollector.ts +79 -1
- package/server/proxy/localeWebProxy.ts +29 -12
- package/server/resolver/database.resolver.ts +82 -31
- package/server/resolver/signal.resolver.ts +67 -28
- package/service/ipcTypes.ts +5 -0
- package/service/predefinedAdaptor/database.adaptor.ts +95 -27
- package/service/predefinedAdaptor/solidSqlite.ts +7 -7
- package/service/predefinedAdaptor/storage.adaptor.ts +35 -9
- package/service/serviceModule.ts +1 -6
- package/signal/base.signal.ts +1 -1
- package/signal/index.ts +1 -0
- package/signal/middleware.ts +5 -1
- package/signal/signalContext.ts +85 -31
- package/signal/signalRegistry.ts +35 -10
- package/signal/trace.ts +279 -0
- package/types/cli/npmRegistry.d.ts +1 -0
- package/types/cli/openBrowser.d.ts +1 -0
- package/types/cli/pluralizeName.d.ts +1 -0
- package/types/cli/semver.d.ts +1 -0
- package/types/client/cookie.d.ts +6 -1
- package/types/common/index.d.ts +1 -0
- package/types/common/jwtDecode.d.ts +2 -0
- package/types/devkit/capacitorApp.d.ts +14 -5
- package/types/devkit/incrementalBuilder/incrementalBuilder.host.d.ts +9 -5
- package/types/document/dataLoader.d.ts +21 -2
- package/types/document/database.d.ts +1 -1
- package/types/server/processMetricsCollector.d.ts +2 -0
- package/types/service/ipcTypes.d.ts +5 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +26 -32
- package/types/service/predefinedAdaptor/solidSqlite.d.ts +3 -3
- package/types/service/predefinedAdaptor/storage.adaptor.d.ts +8 -2
- package/types/service/serviceModule.d.ts +1 -1
- package/types/signal/index.d.ts +1 -0
- package/types/signal/signalContext.d.ts +4 -1
- package/types/signal/signalRegistry.d.ts +25 -4
- package/types/signal/trace.d.ts +97 -0
- package/types/ui/Signal/style.d.ts +15 -0
- package/ui/Signal/Arg.tsx +22 -15
- package/ui/Signal/Doc.tsx +30 -24
- package/ui/Signal/Listener.tsx +15 -39
- package/ui/Signal/Message.tsx +32 -50
- package/ui/Signal/Object.tsx +16 -13
- package/ui/Signal/PubSub.tsx +29 -47
- package/ui/Signal/Response.tsx +7 -17
- package/ui/Signal/RestApi.tsx +41 -57
- package/ui/Signal/WebSocket.tsx +1 -1
- package/ui/Signal/style.ts +36 -0
- package/webkit/useCsrValues.ts +147 -37
|
@@ -15,6 +15,7 @@ export interface UploadRequest {
|
|
|
15
15
|
};
|
|
16
16
|
rename?: string;
|
|
17
17
|
host?: string;
|
|
18
|
+
access?: "public" | "private";
|
|
18
19
|
}
|
|
19
20
|
export interface CopyRequest {
|
|
20
21
|
bucket: string;
|
|
@@ -28,6 +29,7 @@ export interface UploadFromStreamRequest {
|
|
|
28
29
|
body: ReadableStream;
|
|
29
30
|
mimetype: string;
|
|
30
31
|
root?: string;
|
|
32
|
+
access?: "public" | "private";
|
|
31
33
|
updateProgress: (progress: {
|
|
32
34
|
loaded?: number;
|
|
33
35
|
total?: number;
|
|
@@ -49,15 +51,18 @@ export interface StorageAdaptor {
|
|
|
49
51
|
saveData(request: DownloadRequest): Promise<LocalFilePath>;
|
|
50
52
|
copyData(request: CopyRequest): Promise<string>;
|
|
51
53
|
deleteData(url: string): Promise<boolean>;
|
|
54
|
+
deleteDataByPath(path: string): Promise<boolean>;
|
|
52
55
|
}
|
|
53
56
|
export interface BlobStorageOptions extends BaseEnv {
|
|
54
57
|
blobStorage?: {
|
|
55
58
|
baseDir?: string;
|
|
59
|
+
privateBaseDir?: string;
|
|
56
60
|
urlPrefix?: string;
|
|
57
61
|
};
|
|
58
62
|
}
|
|
59
63
|
declare const BlobStorage_base: import("..").AdaptorCls<{}, {
|
|
60
64
|
root: import("..").InjectInfo<"env", string, BlobStorageOptions, never>;
|
|
65
|
+
privateRoot: import("..").InjectInfo<"env", string, BlobStorageOptions, never>;
|
|
61
66
|
urlPrefix: import("..").InjectInfo<"env", string | undefined, BlobStorageOptions, never>;
|
|
62
67
|
}>;
|
|
63
68
|
export declare class BlobStorage extends BlobStorage_base implements StorageAdaptor {
|
|
@@ -65,10 +70,11 @@ export declare class BlobStorage extends BlobStorage_base implements StorageAdap
|
|
|
65
70
|
readData(path: string): Promise<ReadableStream>;
|
|
66
71
|
readDataAsJson<T>(path: string): Promise<T>;
|
|
67
72
|
getDataList(prefix?: string): Promise<string[]>;
|
|
68
|
-
uploadDataFromLocal({ path, localPath, meta }: UploadRequest): Promise<string>;
|
|
69
|
-
uploadDataFromStream({ path, body, mimetype, updateProgress, uploadSuccess }: UploadFromStreamRequest): Promise<void>;
|
|
73
|
+
uploadDataFromLocal({ path, localPath, meta, access }: UploadRequest): Promise<string>;
|
|
74
|
+
uploadDataFromStream({ path, body, mimetype, updateProgress, uploadSuccess, access, }: UploadFromStreamRequest): Promise<void>;
|
|
70
75
|
saveData({ path, localPath, renamePath }: DownloadRequest): Promise<LocalFilePath>;
|
|
71
76
|
copyData({ copyPath, pastePath, host }: CopyRequest): Promise<string>;
|
|
77
|
+
deleteDataByPath(path: string): Promise<boolean>;
|
|
72
78
|
deleteData(url: string): Promise<boolean>;
|
|
73
79
|
}
|
|
74
80
|
export {};
|
|
@@ -17,7 +17,7 @@ export declare class ServiceModel<Srv extends ServiceCls = ServiceCls, CnstModel
|
|
|
17
17
|
});
|
|
18
18
|
static fromModel<Srv extends ServiceCls, CnstModel extends ConstantModel, DbModel extends DatabaseModel>(srv: Srv, cnst: CnstModel, db: DbModel): ServiceModel<Srv, CnstModel, DbModel>;
|
|
19
19
|
static from<Srv extends ServiceCls>(srv: Srv): ServiceModel<Srv, never, never, { [K in `${Uncapitalize<Srv["refName"]>}Service`]: UnCls<Srv>; }>;
|
|
20
|
-
with<SrvModules extends ServiceModel[]>(...srvs: SrvModules): ServiceModel<Srv
|
|
20
|
+
with<SrvModules extends ServiceModel[]>(...srvs: SrvModules): ServiceModel<Srv, CnstModel, DbModel, SrvMap & MergeAllKeyOfObjects<SrvModules, "srvMap">>;
|
|
21
21
|
static getDefaultDbServiceMethods(className: string): {
|
|
22
22
|
[x: string]: ((this: DatabaseService, query: QueryOf<any>, queryOption?: ListQueryOption) => Promise<any>) | ((this: DatabaseService, query: QueryOf<any>, queryOption?: FindQueryOption) => Promise<any>) | ((this: DatabaseService, type: SaveEventType, listener: (doc: Doc, type: CRUDEventType) => PromiseOrObject<void>) => any) | ((this: DatabaseService, id: string, data: DataInputOf) => Promise<any>);
|
|
23
23
|
__get(this: DatabaseService, id: string): Promise<any>;
|
package/types/signal/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type ConstantFieldTypeInput } from "akanjs/constant";
|
|
|
3
3
|
import type { Adaptor, AdaptorCls, DatabaseService, InjectRegistry, LiveRegistry } from "akanjs/service";
|
|
4
4
|
import type { Internal, InternalInfo, MiddlewareCls } from ".";
|
|
5
5
|
import type { EndpointInfo } from "./endpointInfo.d.ts";
|
|
6
|
+
import { SignalTrace } from "./trace.d.ts";
|
|
6
7
|
export type SignalTransportType = "http" | "websocket";
|
|
7
8
|
interface WebSocketRequest {
|
|
8
9
|
ws: Bun.ServerWebSocket<unknown>;
|
|
@@ -19,6 +20,7 @@ export declare class SignalContext<Ctx extends HttpExecutionContext | WebSocketE
|
|
|
19
20
|
adaptor: Adaptor;
|
|
20
21
|
args: unknown[];
|
|
21
22
|
internalArgs: unknown[];
|
|
23
|
+
trace: SignalTrace | null;
|
|
22
24
|
constructor(key: string, reqOrWsReq: Bun.BunRequest | WebSocketRequest, { endpointInfo, adaptor, registry, env, live, middleware, }: {
|
|
23
25
|
endpointInfo: EndpointInfo;
|
|
24
26
|
adaptor: Adaptor;
|
|
@@ -48,6 +50,7 @@ export declare class SignalContext<Ctx extends HttpExecutionContext | WebSocketE
|
|
|
48
50
|
getEnv(): Env;
|
|
49
51
|
}
|
|
50
52
|
export declare class HttpExecutionContext<Appended = unknown> {
|
|
53
|
+
#private;
|
|
51
54
|
req: Bun.BunRequest & Appended;
|
|
52
55
|
res: {
|
|
53
56
|
new (body?: BodyInit | null, init?: ResponseInit): Response;
|
|
@@ -56,11 +59,11 @@ export declare class HttpExecutionContext<Appended = unknown> {
|
|
|
56
59
|
json(data: any, init?: ResponseInit): Response;
|
|
57
60
|
redirect(url: string | URL, status?: number): Response;
|
|
58
61
|
};
|
|
59
|
-
url: URL;
|
|
60
62
|
params: RuntimeRecord;
|
|
61
63
|
searchParams: RuntimeRecord;
|
|
62
64
|
body: RuntimeRecord;
|
|
63
65
|
constructor(req: Bun.BunRequest);
|
|
66
|
+
get url(): URL;
|
|
64
67
|
getArgs(endpointInfo: EndpointInfo): Promise<unknown[]>;
|
|
65
68
|
makeResponse(result: unknown, endpointInfo: EndpointInfo): Response;
|
|
66
69
|
}
|
|
@@ -3,6 +3,14 @@ import type { InternalCls } from "./internal.d.ts";
|
|
|
3
3
|
import type { ServerSignalCls } from "./serverSignal.d.ts";
|
|
4
4
|
import type { SliceCls } from "./slice.d.ts";
|
|
5
5
|
import type { SerializedSignal } from "./types.d.ts";
|
|
6
|
+
type SignalBaseRef<SigCls> = SigCls extends {
|
|
7
|
+
srv: {
|
|
8
|
+
srv: {
|
|
9
|
+
refName: infer RefName;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
} ? RefName : never;
|
|
13
|
+
type SignalWithBase<RefName extends string, SigCls> = SignalBaseRef<SigCls> extends RefName ? SigCls : never;
|
|
6
14
|
export declare class DatabaseSignal<IntlCls extends InternalCls = InternalCls, EndpCls extends EndpointCls = EndpointCls, SlceCls extends SliceCls = SliceCls, SrvrCls extends ServerSignalCls = ServerSignalCls> {
|
|
7
15
|
internal: IntlCls;
|
|
8
16
|
endpoint: EndpCls;
|
|
@@ -21,8 +29,21 @@ export declare class ServiceSignal<IntlCls extends InternalCls = InternalCls, En
|
|
|
21
29
|
/** Registry for database and service signals used by routing and fetch serialization. */
|
|
22
30
|
export declare class SignalRegistry {
|
|
23
31
|
#private;
|
|
24
|
-
static registerDatabase<IntlCls extends InternalCls, EndpCls extends EndpointCls, SlceCls extends SliceCls, SrvrCls extends ServerSignalCls>(internal:
|
|
25
|
-
static getDatabase(refName: string): DatabaseSignal<
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
static registerDatabase<RefName extends string, IntlCls extends InternalCls, EndpCls extends EndpointCls, SlceCls extends SliceCls, SrvrCls extends ServerSignalCls>(refName: RefName, internal: SignalWithBase<RefName, IntlCls>, endpoint: SignalWithBase<RefName, EndpCls>, slice: SignalWithBase<RefName, SlceCls>, server: SrvrCls): DatabaseSignal<IntlCls, EndpCls, SlceCls, SrvrCls>;
|
|
33
|
+
static getDatabase(refName: string): DatabaseSignal<InternalCls, EndpointCls<import("../service.d.ts").ServiceModel<import("../service.d.ts").ServiceCls, any, any, {
|
|
34
|
+
[x: `${Uncapitalize<string>}Service`]: import("../service.d.ts").ExtractInjectInfoObject<{}> & import("../service.d.ts").Service;
|
|
35
|
+
}>, {
|
|
36
|
+
[key: string]: import("./endpointInfo.d.ts").EndpointInfo<import("./endpointInfo.d.ts").EndpointType, {
|
|
37
|
+
[key: string]: any;
|
|
38
|
+
}, any, any, any, any, import("../constant.d.ts").ConstantFieldTypeInput, never, never, boolean>;
|
|
39
|
+
}>, SliceCls, ServerSignalCls> | undefined;
|
|
40
|
+
static registerService<RefName extends string, IntlCls extends InternalCls, EndpCls extends EndpointCls, SrvrCls extends ServerSignalCls>(refName: RefName, internal: SignalWithBase<RefName, IntlCls>, endpoint: SignalWithBase<RefName, EndpCls>, server: SrvrCls): ServiceSignal<IntlCls, EndpCls, SrvrCls>;
|
|
41
|
+
static getService(refName: string): ServiceSignal<InternalCls, EndpointCls<import("../service.d.ts").ServiceModel<import("../service.d.ts").ServiceCls, any, any, {
|
|
42
|
+
[x: `${Uncapitalize<string>}Service`]: import("../service.d.ts").ExtractInjectInfoObject<{}> & import("../service.d.ts").Service;
|
|
43
|
+
}>, {
|
|
44
|
+
[key: string]: import("./endpointInfo.d.ts").EndpointInfo<import("./endpointInfo.d.ts").EndpointType, {
|
|
45
|
+
[key: string]: any;
|
|
46
|
+
}, any, any, any, any, import("../constant.d.ts").ConstantFieldTypeInput, never, never, boolean>;
|
|
47
|
+
}>, ServerSignalCls> | undefined;
|
|
28
48
|
}
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Whether request tracing is enabled. Cached after first read. */
|
|
2
|
+
export declare const isTraceEnabled: () => boolean;
|
|
3
|
+
/** Override the trace flag at runtime (tests / harness control). */
|
|
4
|
+
export declare const setTraceEnabled: (enabled: boolean) => void;
|
|
5
|
+
export interface SpanRecord {
|
|
6
|
+
name: string;
|
|
7
|
+
durationMs: number;
|
|
8
|
+
}
|
|
9
|
+
/** Per-request trace context. Threaded via {@link AsyncLocalStorage}. */
|
|
10
|
+
export declare class SignalTrace {
|
|
11
|
+
#private;
|
|
12
|
+
readonly traceId: string;
|
|
13
|
+
readonly endpointKey: string;
|
|
14
|
+
readonly endpointType: string;
|
|
15
|
+
readonly startedAt: number;
|
|
16
|
+
readonly spans: SpanRecord[];
|
|
17
|
+
dbQueryCount: number;
|
|
18
|
+
dbQueryMs: number;
|
|
19
|
+
cacheHits: number;
|
|
20
|
+
cacheMisses: number;
|
|
21
|
+
dataLoaderBatchCount: number;
|
|
22
|
+
dataLoaderKeyCount: number;
|
|
23
|
+
constructor(endpointKey: string, endpointType: string);
|
|
24
|
+
recordSpan(name: string, durationMs: number): void;
|
|
25
|
+
countDbQuery(durationMs: number): void;
|
|
26
|
+
countCache(hit: boolean): void;
|
|
27
|
+
countDataLoaderBatch(keyCount: number): void;
|
|
28
|
+
finalize(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare const getCurrentTrace: () => SignalTrace | undefined;
|
|
31
|
+
/** Run `fn` with `trace` as the ambient request trace. */
|
|
32
|
+
export declare const runWithTrace: <T>(trace: SignalTrace, fn: () => T) => T;
|
|
33
|
+
/**
|
|
34
|
+
* Time an async stage under the current trace. When tracing is off (or no trace is
|
|
35
|
+
* active) this is a thin passthrough with no measurement overhead.
|
|
36
|
+
*/
|
|
37
|
+
export declare const traceSpan: <T>(name: string, fn: () => Promise<T>) => Promise<T>;
|
|
38
|
+
/** Record a DB query duration against the current trace (no-op when untraced). */
|
|
39
|
+
export declare const traceDbQuery: (durationMs: number) => void;
|
|
40
|
+
/** Record a cache hit/miss against the current trace (no-op when untraced). */
|
|
41
|
+
export declare const traceCache: (hit: boolean) => void;
|
|
42
|
+
/** Record a DataLoader batch against the current trace (no-op when untraced). */
|
|
43
|
+
export declare const traceDataLoaderBatch: (keyCount: number) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Process-wide aggregator. Keeps rolling per-endpoint, per-span statistics with a
|
|
46
|
+
* bounded sample ring per span so percentiles stay representative of steady state
|
|
47
|
+
* without unbounded memory growth.
|
|
48
|
+
*/
|
|
49
|
+
declare class TraceAggregator {
|
|
50
|
+
#private;
|
|
51
|
+
ingest(trace: SignalTrace): void;
|
|
52
|
+
reset(): void;
|
|
53
|
+
/** Summarized snapshot suitable for JSON exposure on the metrics endpoint. */
|
|
54
|
+
snapshot(): {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
endpoints: {
|
|
57
|
+
endpoint: string;
|
|
58
|
+
requests: number;
|
|
59
|
+
avgDbQueriesPerRequest: number;
|
|
60
|
+
avgDbQueryMsPerRequest: number;
|
|
61
|
+
cacheHitRatio: number | null;
|
|
62
|
+
avgDataLoaderBatchSize: number | null;
|
|
63
|
+
spans: {
|
|
64
|
+
name: string;
|
|
65
|
+
count: number;
|
|
66
|
+
meanMs: number;
|
|
67
|
+
p50Ms: number;
|
|
68
|
+
p95Ms: number;
|
|
69
|
+
p99Ms: number;
|
|
70
|
+
maxMs: number;
|
|
71
|
+
}[];
|
|
72
|
+
}[];
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export declare const traceAggregator: TraceAggregator;
|
|
76
|
+
/** Snapshot of all aggregated trace stats. Safe to call when tracing is disabled. */
|
|
77
|
+
export declare const getTraceSnapshot: () => {
|
|
78
|
+
enabled: boolean;
|
|
79
|
+
endpoints: {
|
|
80
|
+
endpoint: string;
|
|
81
|
+
requests: number;
|
|
82
|
+
avgDbQueriesPerRequest: number;
|
|
83
|
+
avgDbQueryMsPerRequest: number;
|
|
84
|
+
cacheHitRatio: number | null;
|
|
85
|
+
avgDataLoaderBatchSize: number | null;
|
|
86
|
+
spans: {
|
|
87
|
+
name: string;
|
|
88
|
+
count: number;
|
|
89
|
+
meanMs: number;
|
|
90
|
+
p50Ms: number;
|
|
91
|
+
p95Ms: number;
|
|
92
|
+
p99Ms: number;
|
|
93
|
+
maxMs: number;
|
|
94
|
+
}[];
|
|
95
|
+
}[];
|
|
96
|
+
};
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const signalUi: {
|
|
2
|
+
sectionTitle: string;
|
|
3
|
+
sectionDescription: string;
|
|
4
|
+
sectionPanel: string;
|
|
5
|
+
endpointCard: string;
|
|
6
|
+
endpointContent: string;
|
|
7
|
+
tablePanel: string;
|
|
8
|
+
inputRow: string;
|
|
9
|
+
inputLabel: string;
|
|
10
|
+
codePanel: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const getEndpointBadgeClassName: (type: string) => "badge badge-primary" | "badge badge-secondary";
|
|
13
|
+
export declare const getGuardBadgeClassName: (guard: string) => "badge badge-primary" | "badge badge-secondary" | "badge";
|
|
14
|
+
export declare const getStatusBadgeClassName: (status: string) => "badge badge-outline" | "badge badge-primary" | "badge badge-error";
|
|
15
|
+
export declare const getStatusTextareaClassName: (status: string) => "" | "border-error text-error" | "border-primary" | "textarea-disabled";
|
package/ui/Signal/Arg.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { AiOutlineDelete } from "react-icons/ai";
|
|
|
9
9
|
import { DatePicker } from "../DatePicker";
|
|
10
10
|
import { Input } from "../Input";
|
|
11
11
|
import UiObject from "./Object";
|
|
12
|
+
import { signalUi } from "./style";
|
|
12
13
|
|
|
13
14
|
interface ArgProps {
|
|
14
15
|
argType: DefaultPrimitiveName;
|
|
@@ -55,7 +56,7 @@ const ArgTable = ({ refName, endpointKey, args }: ArgTableProps) => {
|
|
|
55
56
|
return (
|
|
56
57
|
<table className="table">
|
|
57
58
|
<thead>
|
|
58
|
-
<tr
|
|
59
|
+
<tr>
|
|
59
60
|
<th>Arg Key</th>
|
|
60
61
|
<th className="text-center">Type</th>
|
|
61
62
|
<th className="text-center">Enum</th>
|
|
@@ -69,7 +70,9 @@ const ArgTable = ({ refName, endpointKey, args }: ArgTableProps) => {
|
|
|
69
70
|
return (
|
|
70
71
|
<tbody className="font-normal" key={idx}>
|
|
71
72
|
<tr>
|
|
72
|
-
<td>
|
|
73
|
+
<td>
|
|
74
|
+
<div className="font-bold">{arg.name}</div>
|
|
75
|
+
</td>
|
|
73
76
|
<td className="text-center">
|
|
74
77
|
<UiObject.Type objRef={argRef as ConstantCls} arrDepth={arg.arrDepth ?? 0} />
|
|
75
78
|
</td>
|
|
@@ -84,7 +87,7 @@ const ArgTable = ({ refName, endpointKey, args }: ArgTableProps) => {
|
|
|
84
87
|
onClick={() => {
|
|
85
88
|
onCopy(opt.toString());
|
|
86
89
|
}}
|
|
87
|
-
className="tooltip tooltip-primary btn btn-
|
|
90
|
+
className="tooltip tooltip-primary btn btn-outline btn-xs"
|
|
88
91
|
>
|
|
89
92
|
{opt}
|
|
90
93
|
</button>
|
|
@@ -95,8 +98,12 @@ const ArgTable = ({ refName, endpointKey, args }: ArgTableProps) => {
|
|
|
95
98
|
"-"
|
|
96
99
|
)}
|
|
97
100
|
</td>
|
|
98
|
-
<td className="text-center">
|
|
99
|
-
|
|
101
|
+
<td className="text-center text-base-content/70">
|
|
102
|
+
{l._(`${refName}.signal.${endpointKey}.arg.${arg.name}`)}
|
|
103
|
+
</td>
|
|
104
|
+
<td className="text-center text-base-content/70">
|
|
105
|
+
{l._(`${refName}.signal.${endpointKey}.arg.${arg.name}.desc`)}
|
|
106
|
+
</td>
|
|
100
107
|
</tr>
|
|
101
108
|
</tbody>
|
|
102
109
|
);
|
|
@@ -118,8 +125,8 @@ const ArgParam = ({ endpointKey, arg, value, onChange }: ArgParamProps) => {
|
|
|
118
125
|
else if ((arg.arrDepth ?? 0) > 0) throw new Error(`Param arg - ${endpointKey}/${arg.name} must not be array`);
|
|
119
126
|
const argType = PrimitiveRegistry.getName(argRef as typeof PrimitiveScalar) as DefaultPrimitiveName;
|
|
120
127
|
return (
|
|
121
|
-
<div className=
|
|
122
|
-
<div className=
|
|
128
|
+
<div className={signalUi.inputRow}>
|
|
129
|
+
<div className={signalUi.inputLabel}>{arg.name}</div>
|
|
123
130
|
<div className="w-full">
|
|
124
131
|
<Arg argType={argType} value={value as string} onChange={onChange} />
|
|
125
132
|
</div>
|
|
@@ -141,8 +148,8 @@ const ArgQuery = ({ endpointKey, arg, value, onChange }: ArgQueryProps) => {
|
|
|
141
148
|
throw new Error(`Query arg - ${endpointKey}/${arg.name} must not be more than 2D array`);
|
|
142
149
|
const argType = PrimitiveRegistry.getName(argRef as typeof PrimitiveScalar) as DefaultPrimitiveName;
|
|
143
150
|
return (
|
|
144
|
-
<div className=
|
|
145
|
-
<div className=
|
|
151
|
+
<div className={signalUi.inputRow}>
|
|
152
|
+
<div className={signalUi.inputLabel}>{arg.name}</div>
|
|
146
153
|
<div className="w-full">
|
|
147
154
|
{(arg.arrDepth ?? 0) > 0 && Array.isArray(value) ? (
|
|
148
155
|
<div>
|
|
@@ -156,7 +163,7 @@ const ArgQuery = ({ endpointKey, arg, value, onChange }: ArgQueryProps) => {
|
|
|
156
163
|
}}
|
|
157
164
|
/>
|
|
158
165
|
<button
|
|
159
|
-
className="btn btn-sm btn-square"
|
|
166
|
+
className="btn btn-outline btn-sm btn-square"
|
|
160
167
|
onClick={() => {
|
|
161
168
|
onChange([...(value.slice(0, idx) as string[]), ...(value.slice(idx + 1) as string[])]);
|
|
162
169
|
}}
|
|
@@ -166,7 +173,7 @@ const ArgQuery = ({ endpointKey, arg, value, onChange }: ArgQueryProps) => {
|
|
|
166
173
|
</div>
|
|
167
174
|
))}
|
|
168
175
|
<button
|
|
169
|
-
className="btn btn-sm"
|
|
176
|
+
className="btn btn-outline btn-sm"
|
|
170
177
|
onClick={() => {
|
|
171
178
|
onChange([...(value as string[]), arg.example]);
|
|
172
179
|
}}
|
|
@@ -195,8 +202,8 @@ const ArgFormData = ({ endpointKey, arg, value, onChange }: ArgFormDataProps) =>
|
|
|
195
202
|
throw new Error(`FormData arg - ${endpointKey}/${arg.name} must be Upload`);
|
|
196
203
|
else if ((arg.arrDepth ?? 0) < 1) throw new Error(`FormData arg - ${endpointKey}/${arg.name} must be array`);
|
|
197
204
|
return (
|
|
198
|
-
<div className=
|
|
199
|
-
<div className=
|
|
205
|
+
<div className={signalUi.inputRow}>
|
|
206
|
+
<div className={signalUi.inputLabel}>{arg.name}</div>
|
|
200
207
|
<div className="w-full">
|
|
201
208
|
<Arg argType="Upload" value={value as FileList} onChange={onChange} />
|
|
202
209
|
</div>
|
|
@@ -320,7 +327,7 @@ const ArgJson = ({ value, onChange }: ArgJsonProps) => {
|
|
|
320
327
|
<Input.TextArea
|
|
321
328
|
validate={(e) => true}
|
|
322
329
|
className="w-full"
|
|
323
|
-
inputClassName="w-full min-h-[300px]"
|
|
330
|
+
inputClassName="w-full min-h-[300px] rounded-xl border border-base-300 bg-base-100"
|
|
324
331
|
value={value}
|
|
325
332
|
onPressEnter={(value) => {
|
|
326
333
|
onChange(value);
|
|
@@ -342,7 +349,7 @@ const ArgUpload = ({ value, onChange }: ArgUploadProps) => {
|
|
|
342
349
|
<input
|
|
343
350
|
type="file"
|
|
344
351
|
multiple
|
|
345
|
-
className="file-input w-full max-w-xs"
|
|
352
|
+
className="file-input file-input-bordered w-full max-w-xs"
|
|
346
353
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
347
354
|
onChange(new Array(e.target.files?.length).fill(0).map((_, idx) => e.target.files?.[idx]) as any as FileList);
|
|
348
355
|
}}
|
package/ui/Signal/Doc.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { fetch, usePage } from "akanjs/client";
|
|
3
|
-
import { lowerlize } from "akanjs/common";
|
|
3
|
+
import { decodeJwtPayload, lowerlize } from "akanjs/common";
|
|
4
4
|
import { type Account, type FetchProxy, getDefaultAccount } from "akanjs/fetch";
|
|
5
5
|
import { st } from "akanjs/store";
|
|
6
|
-
import { jwtDecode } from "jwt-decode";
|
|
7
6
|
import { type ReactNode, useEffect, useState } from "react";
|
|
8
7
|
import { AiOutlineApi, AiOutlineCopy } from "react-icons/ai";
|
|
9
8
|
import { BiLock } from "react-icons/bi";
|
|
@@ -11,6 +10,7 @@ import { Copy } from "../Copy";
|
|
|
11
10
|
import { Input } from "../Input";
|
|
12
11
|
import { Modal } from "../Modal";
|
|
13
12
|
import RestApi from "./RestApi";
|
|
13
|
+
import { signalUi } from "./style";
|
|
14
14
|
import WebSocket from "./WebSocket";
|
|
15
15
|
|
|
16
16
|
export default function Doc() {
|
|
@@ -27,7 +27,6 @@ const DocSetting = ({
|
|
|
27
27
|
roleTypes = ["Public", "User", "Admin", "SuperAdmin"],
|
|
28
28
|
roleKeys = { me: "Admin", self: "User" },
|
|
29
29
|
}: DocSettingProps) => {
|
|
30
|
-
const trySignalType = st.use.trySignalType();
|
|
31
30
|
const tryRoles = st.use.tryRoles();
|
|
32
31
|
const tryAccount = st.use.tryAccount();
|
|
33
32
|
useEffect(() => {
|
|
@@ -39,9 +38,9 @@ const DocSetting = ({
|
|
|
39
38
|
.filter(([key, roleType]) => !!tryAccount[key as keyof typeof tryAccount])
|
|
40
39
|
.map(([key, roleType]) => roleType);
|
|
41
40
|
return (
|
|
42
|
-
<div className="flex w-full flex-wrap items-center justify-between gap-
|
|
43
|
-
<div className="flex flex-1 items-center gap-
|
|
44
|
-
|
|
41
|
+
<div className="flex w-full flex-wrap items-center justify-between gap-3 rounded-xl bg-base-200 p-3">
|
|
42
|
+
<div className="flex flex-1 flex-wrap items-center gap-2">
|
|
43
|
+
<span className="font-semibold text-base-content/70 text-sm">BaseURL</span>
|
|
45
44
|
<Copy text={baseUrl}>
|
|
46
45
|
<button className="btn btn-outline btn-sm">
|
|
47
46
|
{baseUrl}
|
|
@@ -49,10 +48,10 @@ const DocSetting = ({
|
|
|
49
48
|
</button>
|
|
50
49
|
</Copy>
|
|
51
50
|
</div>
|
|
52
|
-
<div className="flex items-center gap-
|
|
53
|
-
Mode
|
|
51
|
+
<div className="flex items-center gap-2">
|
|
52
|
+
<span className="font-semibold text-base-content/70 text-sm">Mode</span>
|
|
54
53
|
<button
|
|
55
|
-
className="btn btn-
|
|
54
|
+
className="btn btn-primary btn-sm"
|
|
56
55
|
onClick={() => {
|
|
57
56
|
st.do.setTrySignalType("restapi");
|
|
58
57
|
}}
|
|
@@ -61,8 +60,8 @@ const DocSetting = ({
|
|
|
61
60
|
Rest API
|
|
62
61
|
</button>
|
|
63
62
|
</div>
|
|
64
|
-
<div className="flex items-center gap-
|
|
65
|
-
For
|
|
63
|
+
<div className="flex flex-wrap items-center gap-1">
|
|
64
|
+
<span className="font-semibold text-base-content/70 text-sm">For</span>
|
|
66
65
|
<button
|
|
67
66
|
className={`btn btn-secondary btn-sm ${tryRoleForAll ? "" : "btn-outline"}`}
|
|
68
67
|
onClick={() => {
|
|
@@ -85,10 +84,10 @@ const DocSetting = ({
|
|
|
85
84
|
</button>
|
|
86
85
|
))}
|
|
87
86
|
</div>
|
|
88
|
-
<div className="flex items-center gap-
|
|
89
|
-
Auth
|
|
87
|
+
<div className="flex items-center gap-2">
|
|
88
|
+
<span className="font-semibold text-base-content/70 text-sm">Auth</span>
|
|
90
89
|
<DocAuthModal>
|
|
91
|
-
<button className={`btn btn-sm ${currentRoles.length > 0 ? "btn-
|
|
90
|
+
<button className={`btn btn-sm ${currentRoles.length > 0 ? "btn-primary" : "btn-outline"} `}>
|
|
92
91
|
<BiLock /> {currentRoles.length > 0 ? currentRoles.join(", ") : "Public"}
|
|
93
92
|
</button>
|
|
94
93
|
</DocAuthModal>
|
|
@@ -105,7 +104,7 @@ const DocAuthModal = ({ children }: DocAuthModalProps) => {
|
|
|
105
104
|
const tryJwt = st.use.tryJwt();
|
|
106
105
|
const [jwt, setJwt] = useState(tryJwt);
|
|
107
106
|
const [modalOpen, setModalOpen] = useState(false);
|
|
108
|
-
const decodedAccount = jwt ?
|
|
107
|
+
const decodedAccount = jwt ? decodeJwtPayload<Account>(jwt) : null;
|
|
109
108
|
const accountStr = JSON.stringify(decodedAccount ?? getDefaultAccount(), null, 2);
|
|
110
109
|
return (
|
|
111
110
|
<>
|
|
@@ -141,11 +140,11 @@ const DocAuthModal = ({ children }: DocAuthModalProps) => {
|
|
|
141
140
|
}
|
|
142
141
|
>
|
|
143
142
|
<div className="w-full">
|
|
144
|
-
<div>Current JWT</div>
|
|
143
|
+
<div className={signalUi.sectionTitle}>Current JWT</div>
|
|
145
144
|
<Input inputClassName="w-full" value={jwt ?? ""} onChange={setJwt} validate={() => true} />
|
|
146
145
|
</div>
|
|
147
146
|
<div className="w-full">
|
|
148
|
-
<div className=
|
|
147
|
+
<div className={signalUi.sectionTitle}>Account Decoded</div>
|
|
149
148
|
<div className="relative">
|
|
150
149
|
<Input.TextArea
|
|
151
150
|
inputClassName="w-full"
|
|
@@ -178,10 +177,10 @@ const DocSignals = ({ fetch }: DocSignalsProps) => {
|
|
|
178
177
|
const signal = fetch.serializedSignal;
|
|
179
178
|
const signalEntries = Object.entries(signal).sort(([keyA], [keyB]) => (lowerlize(keyA) > lowerlize(keyB) ? 1 : -1));
|
|
180
179
|
return (
|
|
181
|
-
<div>
|
|
180
|
+
<div className="flex flex-col gap-3">
|
|
182
181
|
{signalEntries.map(([refName, signal], idx) => {
|
|
183
182
|
return (
|
|
184
|
-
<div className="
|
|
183
|
+
<div className="font-bold text-3xl" key={idx}>
|
|
185
184
|
<DocSignal refName={refName} fetch={fetch} />
|
|
186
185
|
</div>
|
|
187
186
|
);
|
|
@@ -200,8 +199,13 @@ const DocSignal = ({ refName, fetch }: DocSignalProps) => {
|
|
|
200
199
|
return (
|
|
201
200
|
<div className="collapse-arrow collapse bg-base-200">
|
|
202
201
|
<input type="checkbox" />
|
|
203
|
-
<div className="collapse-title
|
|
204
|
-
|
|
202
|
+
<div className="collapse-title">
|
|
203
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
204
|
+
<div className="font-bold text-xl">{refName}</div>
|
|
205
|
+
<div className="badge badge-primary">Signal</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div className="collapse-content flex flex-col gap-3">
|
|
205
209
|
<RestApi.Endpoints refName={refName} fetch={fetch} />
|
|
206
210
|
</div>
|
|
207
211
|
</div>
|
|
@@ -218,10 +222,12 @@ const Zone = ({ refName, fetch, openAll }: ZoneProps) => {
|
|
|
218
222
|
const { l } = usePage();
|
|
219
223
|
return (
|
|
220
224
|
<div className="flex break-after-page flex-col gap-4">
|
|
221
|
-
<div
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
<div>
|
|
226
|
+
<div className="font-bold text-3xl">{refName}</div>
|
|
227
|
+
<div className="text-base-content/70">{l._(`${refName}.modelDesc`)}</div>
|
|
228
|
+
</div>
|
|
224
229
|
<DocSetting />
|
|
230
|
+
<div className="font-bold text-2xl">APIs</div>
|
|
225
231
|
<RestApi.Endpoints refName={refName} fetch={fetch} openAll={openAll} />
|
|
226
232
|
<div className="font-bold text-2xl">Web Socket</div>
|
|
227
233
|
<WebSocket.Endpoints refName={refName} fetch={fetch} openAll={openAll} />
|
package/ui/Signal/Listener.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { clsx } from "akanjs/client";
|
|
4
3
|
import { capitalize } from "akanjs/common";
|
|
5
4
|
import { useEffect, useRef } from "react";
|
|
5
|
+
import { getStatusBadgeClassName, getStatusTextareaClassName, signalUi } from "./style";
|
|
6
6
|
|
|
7
7
|
export default function Listener() {
|
|
8
8
|
return <div></div>;
|
|
@@ -23,51 +23,27 @@ const ListenerResult = ({ status, data }: ListenerResultProps) => {
|
|
|
23
23
|
<div className="relative">
|
|
24
24
|
<textarea
|
|
25
25
|
ref={ref}
|
|
26
|
-
className={
|
|
27
|
-
status === "
|
|
28
|
-
|
|
29
|
-
: status === "error"
|
|
30
|
-
? "textarea-error border-error text-error"
|
|
31
|
-
: status === "listening"
|
|
32
|
-
? "textarea-success animate-borderPulse-50 border-3 border-info"
|
|
33
|
-
: ""
|
|
34
|
-
} min-h-[300px] w-full rounded-md bg-base-100 p-4 font-normal text-sm`}
|
|
26
|
+
className={`${signalUi.codePanel} duration-300 ${
|
|
27
|
+
status === "listening" ? "animate-borderPulse-50 border-2" : ""
|
|
28
|
+
} ${getStatusTextareaClassName(status)}`}
|
|
35
29
|
value={dataStr}
|
|
36
30
|
onChange={() => true}
|
|
37
31
|
/>
|
|
38
32
|
|
|
39
|
-
<div className="absolute top-4 right-4 flex items-center justify-center gap-
|
|
40
|
-
{capitalize(status)}
|
|
41
|
-
|
|
33
|
+
<div className="absolute top-4 right-4 flex items-center justify-center gap-2 rounded-lg bg-base-200/80 px-2 py-1 font-bold">
|
|
34
|
+
<span className={getStatusBadgeClassName(status)}>{capitalize(status)}</span>
|
|
42
35
|
<div
|
|
43
|
-
className={
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
className={`size-[10px] rounded-full ${
|
|
37
|
+
status === "error"
|
|
38
|
+
? "bg-error"
|
|
39
|
+
: status === "listening"
|
|
40
|
+
? "animate-pop-300 bg-primary"
|
|
41
|
+
: status === "loading"
|
|
42
|
+
? "animate-ping bg-secondary"
|
|
43
|
+
: "bg-base-300"
|
|
44
|
+
}`}
|
|
50
45
|
></div>
|
|
51
46
|
</div>
|
|
52
|
-
{/* {status === "loading" ? (
|
|
53
|
-
<div className="animate-fadeIn absolute inset-0 flex items-center justify-center backdrop-blur-sm">
|
|
54
|
-
<span className="loading loading-dots loading-lg"></span>
|
|
55
|
-
</div>
|
|
56
|
-
) : status === "idle" ? (
|
|
57
|
-
<></>
|
|
58
|
-
) : status === "listening" ? (
|
|
59
|
-
<div className="absolute right-4 top-4 w-3 h-3">
|
|
60
|
-
<div className="rounded-full w-full h-full animate-bounce bg-success"></div>
|
|
61
|
-
</div>
|
|
62
|
-
) : (
|
|
63
|
-
<div className="absolute right-4 top-4">
|
|
64
|
-
<Copy text={dataStr}>
|
|
65
|
-
<button className="btn btn-sm">
|
|
66
|
-
<AiOutlineCopy /> Copy
|
|
67
|
-
</button>
|
|
68
|
-
</Copy>
|
|
69
|
-
</div>
|
|
70
|
-
)} */}
|
|
71
47
|
</div>
|
|
72
48
|
);
|
|
73
49
|
};
|