@feasibleone/blong 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/types.d.ts +106 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/types.ts +120 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.16.0](https://github.com/feasibleone/blong/compare/blong-v1.15.0...blong-v1.16.0) (2026-04-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* blong-ui ([9ca1281](https://github.com/feasibleone/blong/commit/9ca1281c0178c69dfc722bc4a5978f649bc1fa0d))
|
|
9
|
+
|
|
10
|
+
## [1.15.0](https://github.com/feasibleone/blong/compare/blong-v1.14.0...blong-v1.15.0) (2026-04-01)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* implement ConfigRuntime for hot configuration reload ([#115](https://github.com/feasibleone/blong/issues/115)) ([61bab7c](https://github.com/feasibleone/blong/commit/61bab7ce8587bf83d72fe80138aaef68943f21c4))
|
|
16
|
+
* unified test and handlers ([#117](https://github.com/feasibleone/blong/issues/117)) ([bf0ed96](https://github.com/feasibleone/blong/commit/bf0ed96c5df3d949fa225dd8a30fc25698a7855a))
|
|
17
|
+
|
|
3
18
|
## [1.14.0](https://github.com/feasibleone/blong/compare/blong-v1.13.0...blong-v1.14.0) (2026-03-29)
|
|
4
19
|
|
|
5
20
|
|
package/dist/types.d.ts
CHANGED
|
@@ -17625,6 +17625,22 @@ export type Config<T, C> = {
|
|
|
17625
17625
|
logLevel: Parameters<ILog["logger"]>[0];
|
|
17626
17626
|
namespace: string | string[];
|
|
17627
17627
|
imports: string | RegExp | (string | RegExp)[];
|
|
17628
|
+
/**
|
|
17629
|
+
* Strip this many dot-separated namespace segments from the incoming method
|
|
17630
|
+
* name before looking up and calling a local handler. Useful when the
|
|
17631
|
+
* adapter handles a namespace prefix (e.g. `backend`) that must be removed
|
|
17632
|
+
* before the real handler name is used.
|
|
17633
|
+
*
|
|
17634
|
+
* Only applied when the method name does NOT contain a `/` separator
|
|
17635
|
+
* (the `/` form is auto-stripped by `methodPath` already).
|
|
17636
|
+
*/
|
|
17637
|
+
stripNamespace?: number;
|
|
17638
|
+
/**
|
|
17639
|
+
* Prepend this namespace segment (dot-separated) to the outgoing method
|
|
17640
|
+
* name when dispatching. This is the dot-notation counterpart of the
|
|
17641
|
+
* existing `destination` field (which uses `/` as separator).
|
|
17642
|
+
*/
|
|
17643
|
+
appendNamespace?: string;
|
|
17628
17644
|
} & T;
|
|
17629
17645
|
export type RemoteMethod = (...params: unknown[]) => Promise<unknown>;
|
|
17630
17646
|
export interface IRemote {
|
|
@@ -17640,6 +17656,7 @@ export interface IRpcServer {
|
|
|
17640
17656
|
unregister: (methods: string[], namespace: string, reply: boolean) => void;
|
|
17641
17657
|
start: () => Promise<IRpcServer>;
|
|
17642
17658
|
stop: () => Promise<IRpcServer>;
|
|
17659
|
+
setAttachCheckpoint?: (fn: ((meta: IMeta) => void) | undefined) => void;
|
|
17643
17660
|
}
|
|
17644
17661
|
export interface ILocal {
|
|
17645
17662
|
register: (methods: object, namespace: string, reply: boolean, pkg: {
|
|
@@ -17683,6 +17700,7 @@ export interface IRegistry {
|
|
|
17683
17700
|
methods: Map<string, Handlers>;
|
|
17684
17701
|
modules: Map<string | symbol, IRegistry[]>;
|
|
17685
17702
|
createPort: (id: string) => Promise<ReturnType<IAdapterFactory>>;
|
|
17703
|
+
getPort: (id: string) => ReturnType<IAdapterFactory> | undefined;
|
|
17686
17704
|
replaceHandlers: (id: string, handlers: object) => Promise<void>;
|
|
17687
17705
|
loadApi: (id: string, def: {
|
|
17688
17706
|
namespace: Record<string, string | string[]>;
|
|
@@ -17725,6 +17743,7 @@ export interface IApi {
|
|
|
17725
17743
|
utLog: {
|
|
17726
17744
|
createLog: ILog["logger"];
|
|
17727
17745
|
};
|
|
17746
|
+
attachCheckpoint?: (meta: IMeta) => void;
|
|
17728
17747
|
handlers?: (api: {
|
|
17729
17748
|
utError: IError;
|
|
17730
17749
|
remote: IRemote;
|
|
@@ -17801,6 +17820,21 @@ export interface IAdapter<T, C> {
|
|
|
17801
17820
|
waiting: unknown;
|
|
17802
17821
|
buffer: unknown;
|
|
17803
17822
|
}) => void;
|
|
17823
|
+
/**
|
|
17824
|
+
* Optional lifecycle hook called when configuration changes.
|
|
17825
|
+
* When present, the framework calls this instead of a full stop+start cycle.
|
|
17826
|
+
* The adapter should inspect `diff` and only recreate the resources that
|
|
17827
|
+
* actually changed (e.g. destroy and rebuild the DB connection pool when
|
|
17828
|
+
* the `knex` sub-key is in the diff, but leave everything else intact).
|
|
17829
|
+
*
|
|
17830
|
+
* @param diff Flat map of dotted config paths to `{prev, next}` pairs
|
|
17831
|
+
* @param next The full new effective config snapshot (via proxy)
|
|
17832
|
+
* @param prev The full previous effective config snapshot
|
|
17833
|
+
*/
|
|
17834
|
+
configChanged?: (diff: Map<string, {
|
|
17835
|
+
prev: unknown;
|
|
17836
|
+
next: unknown;
|
|
17837
|
+
}>, next: object, prev: object) => Promise<void>;
|
|
17804
17838
|
}
|
|
17805
17839
|
export interface IAdapterFactory<T = Record<string, unknown>, C = Record<string, unknown>> {
|
|
17806
17840
|
config?: Config<T, C> | false;
|
|
@@ -17878,6 +17912,13 @@ export interface IMeta {
|
|
|
17878
17912
|
};
|
|
17879
17913
|
gateway?: object;
|
|
17880
17914
|
validation?: unknown;
|
|
17915
|
+
name?: string;
|
|
17916
|
+
checkpoint?: CheckpointFn;
|
|
17917
|
+
checkpoints?: Array<{
|
|
17918
|
+
name: string;
|
|
17919
|
+
data?: unknown;
|
|
17920
|
+
timestamp: number;
|
|
17921
|
+
}>;
|
|
17881
17922
|
}
|
|
17882
17923
|
export type HRTime = [
|
|
17883
17924
|
number,
|
|
@@ -18001,18 +18042,22 @@ export type ThenableProxy = Promise<unknown> & {
|
|
|
18001
18042
|
export type ChainStep = ((assert: typeof Assert, context: {
|
|
18002
18043
|
$meta: IMeta;
|
|
18003
18044
|
} & Record<string, Promise<unknown[]> & ThenableProxy>) => Promise<object>) | object;
|
|
18045
|
+
export type CheckpointFn = (this: IMeta, name: string, data?: unknown) => void;
|
|
18004
18046
|
export interface ILib {
|
|
18005
18047
|
type: typeof Type;
|
|
18006
18048
|
error: <T>(errors: T) => Record<keyof T, (params?: unknown, $meta?: IMeta) => ITypedError>;
|
|
18007
18049
|
rename: <T extends object>(object: T, name: string) => T & {
|
|
18008
18050
|
name: string;
|
|
18009
18051
|
};
|
|
18052
|
+
/** @deprecated The framework now auto-names step arrays from handler names. */
|
|
18010
18053
|
group: (name: string) => (handlers: ChainStep[]) => ChainStep[] & {
|
|
18011
18054
|
name: string;
|
|
18012
18055
|
};
|
|
18056
|
+
assert: typeof Assert | undefined;
|
|
18013
18057
|
ulid: () => string;
|
|
18014
18058
|
uuid4: () => string;
|
|
18015
18059
|
uuid7: () => string;
|
|
18060
|
+
setProperty: (obj: Record<string, unknown>, path: string, value: unknown) => void;
|
|
18016
18061
|
merge<T, S1>(target: T, source: S1): T & S1;
|
|
18017
18062
|
merge<T, S1, S2>(target: T, source1: S1, source2: S2): T & S1 & S2;
|
|
18018
18063
|
merge<T, S1, S2, S3>(target: T, source1: S1, source2: S2, source3: S3): T & S1 & S2 & S3;
|
|
@@ -18109,6 +18154,65 @@ export declare abstract class Internal {
|
|
|
18109
18154
|
start(...params: unknown[]): Promise<unknown>;
|
|
18110
18155
|
}
|
|
18111
18156
|
export declare const handler: <T = Record<string, unknown>, C = AdapterContext>(definition: Definition<T, C>) => Definition<T, C>;
|
|
18157
|
+
/**
|
|
18158
|
+
* Browser-side equivalent of `handler`. Use this to define a component handler
|
|
18159
|
+
* in a realm's `component/` layer. Functionally identical to `handler` — the
|
|
18160
|
+
* distinction is semantic and makes intent clear in code review.
|
|
18161
|
+
*
|
|
18162
|
+
* The inner function should return a map whose keys are dot-notation method names
|
|
18163
|
+
* (e.g. `'coral.browse'`) and values are async functions that return component
|
|
18164
|
+
* metadata `{title, permission, icon, component: async () => ReactComponent}`.
|
|
18165
|
+
*
|
|
18166
|
+
* @example
|
|
18167
|
+
* ```ts
|
|
18168
|
+
* export default componentHandler(blong => function coralBrowse() {
|
|
18169
|
+
* return {
|
|
18170
|
+
* 'coral.browse': async () => ({
|
|
18171
|
+
* title: 'Browse Corals',
|
|
18172
|
+
* permission: 'marine.coral.browse',
|
|
18173
|
+
* component: async () => (await import('./CoralBrowse.js')).default,
|
|
18174
|
+
* }),
|
|
18175
|
+
* };
|
|
18176
|
+
* });
|
|
18177
|
+
* ```
|
|
18178
|
+
*/
|
|
18179
|
+
export declare const componentHandler: <T = Record<string, unknown>, C = AdapterContext>(definition: Definition<T, C>) => Definition<T, C>;
|
|
18180
|
+
/** Action definition for use with `defineActions`. */
|
|
18181
|
+
export interface IActionDef {
|
|
18182
|
+
title?: string;
|
|
18183
|
+
permission?: string;
|
|
18184
|
+
icon?: string;
|
|
18185
|
+
/** Lazy-load a page component (page action). */
|
|
18186
|
+
component?: () => Promise<unknown>;
|
|
18187
|
+
/** Bus method name to invoke (query or mutation action). */
|
|
18188
|
+
method?: string;
|
|
18189
|
+
/** When true the action mutates data and should invalidate related caches. */
|
|
18190
|
+
mutates?: boolean;
|
|
18191
|
+
/** Action names whose query caches should be invalidated on success. */
|
|
18192
|
+
invalidates?: string[];
|
|
18193
|
+
/** Static params merged into every invocation. */
|
|
18194
|
+
params?: Record<string, unknown> | ((params: Record<string, unknown>) => Record<string, unknown>);
|
|
18195
|
+
}
|
|
18196
|
+
/**
|
|
18197
|
+
* Register action metadata for a realm's browser layer.
|
|
18198
|
+
*
|
|
18199
|
+
* Returns a handler-compatible function that the blong framework loads from
|
|
18200
|
+
* the realm's `actions/` or `action/` folder. Each entry in `actions` is
|
|
18201
|
+
* wrapped in a no-argument function so the framework can call it as a method.
|
|
18202
|
+
*
|
|
18203
|
+
* @example
|
|
18204
|
+
* ```ts
|
|
18205
|
+
* // marine/actions/index.ts
|
|
18206
|
+
* export default defineActions({
|
|
18207
|
+
* 'marine.coral.browse': {
|
|
18208
|
+
* title: 'Browse Corals',
|
|
18209
|
+
* permission: 'marine.coral.browse',
|
|
18210
|
+
* component: () => import('./components/CoralBrowse.js'),
|
|
18211
|
+
* },
|
|
18212
|
+
* });
|
|
18213
|
+
* ```
|
|
18214
|
+
*/
|
|
18215
|
+
export declare const defineActions: (actions: Record<string, IActionDef>) => ((_blong: unknown) => Record<string, () => IActionDef>);
|
|
18112
18216
|
export declare const library: <T = Record<string, unknown>>(definition: Lib<T>) => Lib<T>;
|
|
18113
18217
|
export declare const validation: (validation: ValidationDefinition) => ValidationDefinition;
|
|
18114
18218
|
export declare const api: (api: ApiDefinition) => ApiDefinition;
|
|
@@ -18125,6 +18229,8 @@ export type Kinds = "lib" | "validation" | "api" | "solution" | "server" | "brow
|
|
|
18125
18229
|
export declare const kind: (what: {}) => Kinds | undefined;
|
|
18126
18230
|
declare const _default: {
|
|
18127
18231
|
handler: <T = Record<string, unknown>, C = AdapterContext>(definition: Definition<T, C>) => Definition<T, C>;
|
|
18232
|
+
componentHandler: <T = Record<string, unknown>, C = AdapterContext>(definition: Definition<T, C>) => Definition<T, C>;
|
|
18233
|
+
defineActions: (actions: Record<string, IActionDef>) => ((_blong: unknown) => Record<string, () => IActionDef>);
|
|
18128
18234
|
library: <T = Record<string, unknown>>(definition: Lib<T>) => Lib<T>;
|
|
18129
18235
|
validation: (validation: ValidationDefinition) => ValidationDefinition;
|
|
18130
18236
|
api: (api: ApiDefinition) => ApiDefinition;
|
package/dist/types.js
CHANGED
|
@@ -21,6 +21,49 @@ export class Internal {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
export const handler = (definition) => Object.defineProperty(definition, Kind, { value: 'handler' });
|
|
24
|
+
/**
|
|
25
|
+
* Browser-side equivalent of `handler`. Use this to define a component handler
|
|
26
|
+
* in a realm's `component/` layer. Functionally identical to `handler` — the
|
|
27
|
+
* distinction is semantic and makes intent clear in code review.
|
|
28
|
+
*
|
|
29
|
+
* The inner function should return a map whose keys are dot-notation method names
|
|
30
|
+
* (e.g. `'coral.browse'`) and values are async functions that return component
|
|
31
|
+
* metadata `{title, permission, icon, component: async () => ReactComponent}`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* export default componentHandler(blong => function coralBrowse() {
|
|
36
|
+
* return {
|
|
37
|
+
* 'coral.browse': async () => ({
|
|
38
|
+
* title: 'Browse Corals',
|
|
39
|
+
* permission: 'marine.coral.browse',
|
|
40
|
+
* component: async () => (await import('./CoralBrowse.js')).default,
|
|
41
|
+
* }),
|
|
42
|
+
* };
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export const componentHandler = (definition) => Object.defineProperty(definition, Kind, { value: 'handler' });
|
|
47
|
+
/**
|
|
48
|
+
* Register action metadata for a realm's browser layer.
|
|
49
|
+
*
|
|
50
|
+
* Returns a handler-compatible function that the blong framework loads from
|
|
51
|
+
* the realm's `actions/` or `action/` folder. Each entry in `actions` is
|
|
52
|
+
* wrapped in a no-argument function so the framework can call it as a method.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* // marine/actions/index.ts
|
|
57
|
+
* export default defineActions({
|
|
58
|
+
* 'marine.coral.browse': {
|
|
59
|
+
* title: 'Browse Corals',
|
|
60
|
+
* permission: 'marine.coral.browse',
|
|
61
|
+
* component: () => import('./components/CoralBrowse.js'),
|
|
62
|
+
* },
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export const defineActions = (actions) => Object.defineProperty((_blong) => Object.fromEntries(Object.entries(actions).map(([key, value]) => [key, () => value])), Kind, { value: 'handler' });
|
|
24
67
|
export const library = (definition) => Object.defineProperty(definition, Kind, { value: 'lib' });
|
|
25
68
|
export const validation = (validation) => Object.defineProperty(validation, Kind, { value: 'validation' });
|
|
26
69
|
export const api = (api) => Object.defineProperty(api, Kind, { value: 'api' });
|
|
@@ -41,6 +84,8 @@ export const orchestrator = (definition) => Object.defineProperty(definition, Ki
|
|
|
41
84
|
export const kind = (what) => what[Kind];
|
|
42
85
|
export default {
|
|
43
86
|
handler,
|
|
87
|
+
componentHandler,
|
|
88
|
+
defineActions,
|
|
44
89
|
library,
|
|
45
90
|
validation,
|
|
46
91
|
api,
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAYA,OAAO,EACH,IAAI,GAWP,MAAM,SAAS,CAAC;AAMjB,OAAO,KAAK,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -113,6 +113,22 @@ export type Config<T, C> = {
|
|
|
113
113
|
logLevel: Parameters<ILog['logger']>[0];
|
|
114
114
|
namespace: string | string[];
|
|
115
115
|
imports: string | RegExp | (string | RegExp)[];
|
|
116
|
+
/**
|
|
117
|
+
* Strip this many dot-separated namespace segments from the incoming method
|
|
118
|
+
* name before looking up and calling a local handler. Useful when the
|
|
119
|
+
* adapter handles a namespace prefix (e.g. `backend`) that must be removed
|
|
120
|
+
* before the real handler name is used.
|
|
121
|
+
*
|
|
122
|
+
* Only applied when the method name does NOT contain a `/` separator
|
|
123
|
+
* (the `/` form is auto-stripped by `methodPath` already).
|
|
124
|
+
*/
|
|
125
|
+
stripNamespace?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Prepend this namespace segment (dot-separated) to the outgoing method
|
|
128
|
+
* name when dispatching. This is the dot-notation counterpart of the
|
|
129
|
+
* existing `destination` field (which uses `/` as separator).
|
|
130
|
+
*/
|
|
131
|
+
appendNamespace?: string;
|
|
116
132
|
} & T;
|
|
117
133
|
|
|
118
134
|
export type RemoteMethod = (...params: unknown[]) => Promise<unknown>;
|
|
@@ -128,6 +144,7 @@ export interface IRpcServer {
|
|
|
128
144
|
unregister: (methods: string[], namespace: string, reply: boolean) => void;
|
|
129
145
|
start: () => Promise<IRpcServer>;
|
|
130
146
|
stop: () => Promise<IRpcServer>;
|
|
147
|
+
setAttachCheckpoint?: (fn: ((meta: IMeta) => void) | undefined) => void;
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
export interface ILocal {
|
|
@@ -172,6 +189,7 @@ export interface IRegistry {
|
|
|
172
189
|
methods: Map<string, Handlers>;
|
|
173
190
|
modules: Map<string | symbol, IRegistry[]>;
|
|
174
191
|
createPort: (id: string) => Promise<ReturnType<IAdapterFactory>>;
|
|
192
|
+
getPort: (id: string) => ReturnType<IAdapterFactory> | undefined;
|
|
175
193
|
replaceHandlers: (id: string, handlers: object) => Promise<void>;
|
|
176
194
|
loadApi: (
|
|
177
195
|
id: string,
|
|
@@ -220,6 +238,7 @@ export interface IApi {
|
|
|
220
238
|
utLog: {
|
|
221
239
|
createLog: ILog['logger'];
|
|
222
240
|
};
|
|
241
|
+
attachCheckpoint?: (meta: IMeta) => void;
|
|
223
242
|
handlers?: (api: {utError: IError; remote: IRemote; type: typeof Type}) => {
|
|
224
243
|
extends?:
|
|
225
244
|
| string
|
|
@@ -303,6 +322,22 @@ export interface IAdapter<T, C> {
|
|
|
303
322
|
what: unknown,
|
|
304
323
|
context: {requests: unknown; waiting: unknown; buffer: unknown},
|
|
305
324
|
) => void;
|
|
325
|
+
/**
|
|
326
|
+
* Optional lifecycle hook called when configuration changes.
|
|
327
|
+
* When present, the framework calls this instead of a full stop+start cycle.
|
|
328
|
+
* The adapter should inspect `diff` and only recreate the resources that
|
|
329
|
+
* actually changed (e.g. destroy and rebuild the DB connection pool when
|
|
330
|
+
* the `knex` sub-key is in the diff, but leave everything else intact).
|
|
331
|
+
*
|
|
332
|
+
* @param diff Flat map of dotted config paths to `{prev, next}` pairs
|
|
333
|
+
* @param next The full new effective config snapshot (via proxy)
|
|
334
|
+
* @param prev The full previous effective config snapshot
|
|
335
|
+
*/
|
|
336
|
+
configChanged?: (
|
|
337
|
+
diff: Map<string, {prev: unknown; next: unknown}>,
|
|
338
|
+
next: object,
|
|
339
|
+
prev: object,
|
|
340
|
+
) => Promise<void>;
|
|
306
341
|
}
|
|
307
342
|
|
|
308
343
|
export interface IAdapterFactory<T = Record<string, unknown>, C = Record<string, unknown>> {
|
|
@@ -382,6 +417,9 @@ export interface IMeta {
|
|
|
382
417
|
};
|
|
383
418
|
gateway?: object;
|
|
384
419
|
validation?: unknown;
|
|
420
|
+
name?: string;
|
|
421
|
+
checkpoint?: CheckpointFn;
|
|
422
|
+
checkpoints?: Array<{name: string; data?: unknown; timestamp: number}>;
|
|
385
423
|
}
|
|
386
424
|
|
|
387
425
|
export type HRTime = [number, number];
|
|
@@ -520,14 +558,19 @@ export type ChainStep =
|
|
|
520
558
|
) => Promise<object>)
|
|
521
559
|
| object;
|
|
522
560
|
|
|
561
|
+
export type CheckpointFn = (this: IMeta, name: string, data?: unknown) => void;
|
|
562
|
+
|
|
523
563
|
export interface ILib {
|
|
524
564
|
type: typeof Type;
|
|
525
565
|
error: <T>(errors: T) => Record<keyof T, (params?: unknown, $meta?: IMeta) => ITypedError>;
|
|
526
566
|
rename: <T extends object>(object: T, name: string) => T & {name: string};
|
|
567
|
+
/** @deprecated The framework now auto-names step arrays from handler names. */
|
|
527
568
|
group: (name: string) => (handlers: ChainStep[]) => ChainStep[] & {name: string};
|
|
569
|
+
assert: typeof Assert | undefined;
|
|
528
570
|
ulid: () => string;
|
|
529
571
|
uuid4: () => string;
|
|
530
572
|
uuid7: () => string;
|
|
573
|
+
setProperty: (obj: Record<string, unknown>, path: string, value: unknown) => void;
|
|
531
574
|
merge<T, S1>(target: T, source: S1): T & S1;
|
|
532
575
|
merge<T, S1, S2>(target: T, source1: S1, source2: S2): T & S1 & S2;
|
|
533
576
|
merge<T, S1, S2, S3>(target: T, source1: S1, source2: S2, source3: S3): T & S1 & S2 & S3;
|
|
@@ -653,6 +696,81 @@ export abstract class Internal {
|
|
|
653
696
|
export const handler = <T = Record<string, unknown>, C = AdapterContext>(
|
|
654
697
|
definition: Definition<T, C>,
|
|
655
698
|
): Definition<T, C> => Object.defineProperty(definition, Kind, {value: 'handler'});
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Browser-side equivalent of `handler`. Use this to define a component handler
|
|
702
|
+
* in a realm's `component/` layer. Functionally identical to `handler` — the
|
|
703
|
+
* distinction is semantic and makes intent clear in code review.
|
|
704
|
+
*
|
|
705
|
+
* The inner function should return a map whose keys are dot-notation method names
|
|
706
|
+
* (e.g. `'coral.browse'`) and values are async functions that return component
|
|
707
|
+
* metadata `{title, permission, icon, component: async () => ReactComponent}`.
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```ts
|
|
711
|
+
* export default componentHandler(blong => function coralBrowse() {
|
|
712
|
+
* return {
|
|
713
|
+
* 'coral.browse': async () => ({
|
|
714
|
+
* title: 'Browse Corals',
|
|
715
|
+
* permission: 'marine.coral.browse',
|
|
716
|
+
* component: async () => (await import('./CoralBrowse.js')).default,
|
|
717
|
+
* }),
|
|
718
|
+
* };
|
|
719
|
+
* });
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
export const componentHandler = <T = Record<string, unknown>, C = AdapterContext>(
|
|
723
|
+
definition: Definition<T, C>,
|
|
724
|
+
): Definition<T, C> => Object.defineProperty(definition, Kind, {value: 'handler'});
|
|
725
|
+
|
|
726
|
+
/** Action definition for use with `defineActions`. */
|
|
727
|
+
export interface IActionDef {
|
|
728
|
+
title?: string;
|
|
729
|
+
permission?: string;
|
|
730
|
+
icon?: string;
|
|
731
|
+
/** Lazy-load a page component (page action). */
|
|
732
|
+
component?: () => Promise<unknown>;
|
|
733
|
+
/** Bus method name to invoke (query or mutation action). */
|
|
734
|
+
method?: string;
|
|
735
|
+
/** When true the action mutates data and should invalidate related caches. */
|
|
736
|
+
mutates?: boolean;
|
|
737
|
+
/** Action names whose query caches should be invalidated on success. */
|
|
738
|
+
invalidates?: string[];
|
|
739
|
+
/** Static params merged into every invocation. */
|
|
740
|
+
params?: Record<string, unknown> | ((params: Record<string, unknown>) => Record<string, unknown>);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Register action metadata for a realm's browser layer.
|
|
745
|
+
*
|
|
746
|
+
* Returns a handler-compatible function that the blong framework loads from
|
|
747
|
+
* the realm's `actions/` or `action/` folder. Each entry in `actions` is
|
|
748
|
+
* wrapped in a no-argument function so the framework can call it as a method.
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* // marine/actions/index.ts
|
|
753
|
+
* export default defineActions({
|
|
754
|
+
* 'marine.coral.browse': {
|
|
755
|
+
* title: 'Browse Corals',
|
|
756
|
+
* permission: 'marine.coral.browse',
|
|
757
|
+
* component: () => import('./components/CoralBrowse.js'),
|
|
758
|
+
* },
|
|
759
|
+
* });
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
export const defineActions = (
|
|
763
|
+
actions: Record<string, IActionDef>,
|
|
764
|
+
): ((_blong: unknown) => Record<string, () => IActionDef>) =>
|
|
765
|
+
Object.defineProperty(
|
|
766
|
+
(_blong: unknown) =>
|
|
767
|
+
Object.fromEntries(
|
|
768
|
+
Object.entries(actions).map(([key, value]) => [key, () => value]),
|
|
769
|
+
),
|
|
770
|
+
Kind,
|
|
771
|
+
{value: 'handler'},
|
|
772
|
+
);
|
|
773
|
+
|
|
656
774
|
export const library = <T = Record<string, unknown>>(definition: Lib<T>): Lib<T> =>
|
|
657
775
|
Object.defineProperty(definition, Kind, {value: 'lib'});
|
|
658
776
|
export const validation = (validation: ValidationDefinition): ValidationDefinition =>
|
|
@@ -709,6 +827,8 @@ export const kind = (what: {[Kind]: Kinds | undefined}): Kinds | undefined => wh
|
|
|
709
827
|
|
|
710
828
|
export default {
|
|
711
829
|
handler,
|
|
830
|
+
componentHandler,
|
|
831
|
+
defineActions,
|
|
712
832
|
library,
|
|
713
833
|
validation,
|
|
714
834
|
api,
|