@owlmeans/client-entrypoint 0.1.3

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.
Files changed (43) hide show
  1. package/README.md +77 -0
  2. package/build/entrypoint.d.ts +6 -0
  3. package/build/entrypoint.d.ts.map +1 -0
  4. package/build/entrypoint.js +62 -0
  5. package/build/entrypoint.js.map +1 -0
  6. package/build/errors.d.ts +10 -0
  7. package/build/errors.d.ts.map +1 -0
  8. package/build/errors.js +17 -0
  9. package/build/errors.js.map +1 -0
  10. package/build/helper.d.ts +8 -0
  11. package/build/helper.d.ts.map +1 -0
  12. package/build/helper.js +37 -0
  13. package/build/helper.js.map +1 -0
  14. package/build/index.d.ts +5 -0
  15. package/build/index.d.ts.map +1 -0
  16. package/build/index.js +4 -0
  17. package/build/index.js.map +1 -0
  18. package/build/types.d.ts +33 -0
  19. package/build/types.d.ts.map +1 -0
  20. package/build/types.js +2 -0
  21. package/build/types.js.map +1 -0
  22. package/build/utils/entrypoint.d.ts +12 -0
  23. package/build/utils/entrypoint.d.ts.map +1 -0
  24. package/build/utils/entrypoint.js +51 -0
  25. package/build/utils/entrypoint.js.map +1 -0
  26. package/build/utils/handler.d.ts +6 -0
  27. package/build/utils/handler.d.ts.map +1 -0
  28. package/build/utils/handler.js +104 -0
  29. package/build/utils/handler.js.map +1 -0
  30. package/build/utils/index.d.ts +3 -0
  31. package/build/utils/index.d.ts.map +1 -0
  32. package/build/utils/index.js +3 -0
  33. package/build/utils/index.js.map +1 -0
  34. package/package.json +56 -0
  35. package/src/entrypoint.ts +88 -0
  36. package/src/errors.ts +23 -0
  37. package/src/helper.ts +55 -0
  38. package/src/index.ts +5 -0
  39. package/src/types.ts +41 -0
  40. package/src/utils/entrypoint.ts +66 -0
  41. package/src/utils/handler.ts +136 -0
  42. package/src/utils/index.ts +3 -0
  43. package/tsconfig.json +10 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @owlmeans/client-entrypoint
2
+
3
+ Client-side entrypoint system: elevates route definitions into API-calling `ClientEntrypoint` instances.
4
+
5
+ ## Overview
6
+
7
+ - `elevate(entrypoints, alias, handler?, opts?)` — attaches a client handler to an entrypoint (mirrors server `elevate`)
8
+ - `ClientEntrypoint<T>` has a `call(request?)` method that resolves the URL and makes an HTTP/WS request
9
+ - `stab` — no-op handler for entrypoints that only need URL resolution (no logic)
10
+ - `provideRequest(alias, path)` — creates an `AbstractRequest` for programmatic entrypoint calls
11
+ - `pickPerSchema(schema, obj)` — extracts fields from an object matching an AJV schema
12
+ - Re-exported as `celevate` from `@owlmeans/server-app`
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add @owlmeans/client-entrypoint
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Define and elevate a client entrypoint for API calls:
23
+
24
+ ```typescript
25
+ import { elevate, stab } from '@owlmeans/client-entrypoint'
26
+ import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
27
+
28
+ const appModules = [
29
+ entrypoint(route('project-list', '/projects', frontend('base'))),
30
+ entrypoint(route('project-create', '/projects', backend(RouteMethod.POST))),
31
+ ]
32
+
33
+ // Frontend navigation — just a stub, no network call
34
+ elevate(appModules, 'project-list', stab)
35
+
36
+ // Backend API call — call() makes a POST request
37
+ elevate(appModules, 'project-create')
38
+ ```
39
+
40
+ Call an entrypoint from a service:
41
+
42
+ ```typescript
43
+ const agentEntrypoint = ctx.entrypoint<ClientEntrypoint<Project>>(agent.project.create)
44
+ const [result] = await agentEntrypoint.call({
45
+ body: { prompt: payload.prompt, entity: req.auth?.entityId }
46
+ })
47
+ ```
48
+
49
+ ## API
50
+
51
+ ### `elevate<T, R>(entrypoints, alias, handler?, opts?): ClientEntrypoint<T, R>[]`
52
+
53
+ Mutates `entrypoints` in-place and returns the array typed as `ClientEntrypoint`. The `handler` sets how `call()` behaves.
54
+
55
+ ### `stab: RefedEntrypointHandler`
56
+
57
+ No-op handler for frontend-only entrypoints that just need URL resolution.
58
+
59
+ ### `ClientEntrypoint<T>` (type)
60
+
61
+ - `call(request?)` — resolves URL, makes HTTP request, returns `[T, EntrypointOutcome]`
62
+ - `validate(request?)` — validates the request against the entrypoint filter schema
63
+ - `getPath(partial?)` — returns the URL path (with or without path params)
64
+
65
+ ### `provideRequest<T>(alias, path): AbstractRequest<T>`
66
+
67
+ Creates a minimal request object for programmatic `call()` invocations.
68
+
69
+ ### `pickPerSchema<T>(schema, obj): Partial<T>`
70
+
71
+ Extracts only the keys present in the AJV schema from `obj`.
72
+
73
+ ## Related Packages
74
+
75
+ - [`@owlmeans/entrypoint`](../entrypoint) — `CommonEntrypoint` base that gets elevated
76
+ - [`@owlmeans/client`](../client) — `useNavigate` uses `ClientEntrypoint.call()`
77
+ - [`@owlmeans/server-app`](../server-app) — re-exports `elevate` as `celevate`
@@ -0,0 +1,6 @@
1
+ import type { ClientRouteModel } from '@owlmeans/client-route';
2
+ import type { ClientEntrypoint, ClientEntrypointOptions, RefedEntrypointHandler } from './types.js';
3
+ import type { AbstractRequest, CommonEntrypoint } from '@owlmeans/entrypoint';
4
+ import type { CommonRouteModel } from '@owlmeans/route';
5
+ export declare const entrypoint: <T, R extends AbstractRequest = AbstractRequest>(arg: CommonEntrypoint | ClientRouteModel | CommonRouteModel, handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean, opts?: ClientEntrypointOptions | boolean) => ClientEntrypoint<T, R>;
6
+ //# sourceMappingURL=entrypoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAiB,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAClH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAO7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAGvD,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EACvE,KAAK,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB,EAC3D,UAAU,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,uBAAuB,GAAG,OAAO,EAC1E,OAAO,uBAAuB,GAAG,OAAO,KACvC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CA+DvB,CAAA"}
@@ -0,0 +1,62 @@
1
+ import { isEntrypoint, makeBasicEntrypoint, normalizeHelperParams, validate } from './utils/entrypoint.js';
2
+ import { isClientRouteModel, route } from '@owlmeans/client-route';
3
+ import { apiCall, apiHandler, urlCall } from './utils/handler.js';
4
+ import { AppType } from '@owlmeans/context';
5
+ import { normalizePath } from '@owlmeans/route';
6
+ import { provideRequest } from './helper.js';
7
+ export const entrypoint = (arg, handler, opts) => {
8
+ const entrypointHandle = { ref: undefined };
9
+ let _entrypoint;
10
+ [handler, opts] = normalizeHelperParams(handler, opts);
11
+ const _handler = handler ??
12
+ (('route' in arg.route ? arg.route.route.type : arg.route.type)
13
+ === AppType.Backend ? apiHandler : undefined);
14
+ if (isEntrypoint(arg)) {
15
+ assertExplicitHandler(arg.route.route.type, handler);
16
+ const routeModel = route(arg.route, opts?.routeOptions);
17
+ _entrypoint = arg;
18
+ _entrypoint.route = routeModel;
19
+ _entrypoint.guards = opts?.guards ?? arg.guards;
20
+ _entrypoint.filter = opts?.filter ?? arg.filter;
21
+ _entrypoint.gate = opts?.gate ?? arg.gate;
22
+ _entrypoint.gateParams = opts?.gateParams ?? arg.gateParams;
23
+ }
24
+ else if (isClientRouteModel(arg)) {
25
+ assertExplicitHandler(arg.route.type, handler);
26
+ _entrypoint = makeBasicEntrypoint(arg, { ...opts });
27
+ _entrypoint.route = arg;
28
+ }
29
+ else {
30
+ assertExplicitHandler(arg.route.type, handler);
31
+ const _route = route(arg, opts?.routeOptions);
32
+ _entrypoint = makeBasicEntrypoint(_route, { ...opts });
33
+ _entrypoint.route = _route;
34
+ }
35
+ _entrypoint.getPath = (partial) => partial === true ? normalizePath(_entrypoint.route.route.partialPath) : _entrypoint.route.route.path;
36
+ _entrypoint.call = handler != null ? urlCall(entrypointHandle, opts) : apiCall(entrypointHandle, opts);
37
+ _entrypoint.request = ((request) => {
38
+ if (entrypointHandle.ref == null) {
39
+ throw SyntaxError(`Try to request uninitialized entrypoint ${JSON.stringify(arg)}`);
40
+ }
41
+ const _request = provideRequest(entrypointHandle.ref.getAlias(), entrypointHandle.ref.getPath());
42
+ request != null && Object.entries(request).forEach(([key, value]) => {
43
+ _request[key] = value;
44
+ });
45
+ return _request;
46
+ });
47
+ _entrypoint.handle = _handler?.(entrypointHandle);
48
+ _entrypoint.validate = validate(entrypointHandle);
49
+ _entrypoint.reinitializeContext = (context) => {
50
+ const newEntrypoint = entrypoint(_entrypoint.route, handler, opts);
51
+ newEntrypoint.ctx = context;
52
+ return newEntrypoint;
53
+ };
54
+ entrypointHandle.ref = _entrypoint;
55
+ return _entrypoint;
56
+ };
57
+ const assertExplicitHandler = (type, handler) => {
58
+ if (type === AppType.Backend && handler != null) {
59
+ throw new SyntaxError('We can\'t provide explicit handler to backend client entrypoint');
60
+ }
61
+ };
62
+ //# sourceMappingURL=entrypoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.js","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { ResilientError } from '@owlmeans/error';
2
+ export declare class ClientEntrypointError extends ResilientError {
3
+ static typeName: string;
4
+ constructor(message: string);
5
+ }
6
+ export declare class ClientValidationError extends ClientEntrypointError {
7
+ static typeName: string;
8
+ constructor(message: string);
9
+ }
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,OAAuB,QAAQ,SAAsB;gBAEzC,OAAO,EAAE,MAAM;CAG5B;AAED,qBAAa,qBAAsB,SAAQ,qBAAqB;IAC9D,OAAuB,QAAQ,SAAgD;gBAEnE,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,17 @@
1
+ import { ResilientError } from '@owlmeans/error';
2
+ export class ClientEntrypointError extends ResilientError {
3
+ static typeName = 'ClientModuleError';
4
+ constructor(message) {
5
+ super(ClientEntrypointError.typeName, `clinet-module:${message}`);
6
+ }
7
+ }
8
+ export class ClientValidationError extends ClientEntrypointError {
9
+ static typeName = `${ClientEntrypointError.typeName}Validation`;
10
+ constructor(message) {
11
+ super(`validation:${message}`);
12
+ this.type = ClientValidationError.typeName;
13
+ }
14
+ }
15
+ ResilientError.registerErrorClass(ClientEntrypointError);
16
+ ResilientError.registerErrorClass(ClientValidationError);
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAO,qBAAsB,SAAQ,cAAc;IAChD,MAAM,CAAU,QAAQ,GAAG,mBAAmB,CAAA;IAErD,YAAY,OAAe;QACzB,KAAK,CAAC,qBAAqB,CAAC,QAAQ,EAAE,iBAAiB,OAAO,EAAE,CAAC,CAAA;IACnE,CAAC;;AAGH,MAAM,OAAO,qBAAsB,SAAQ,qBAAqB;IACvD,MAAM,CAAU,QAAQ,GAAG,GAAG,qBAAqB,CAAC,QAAQ,YAAY,CAAA;IAE/E,YAAY,OAAe;QACzB,KAAK,CAAC,cAAc,OAAO,EAAE,CAAC,CAAA;QAC9B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC,QAAQ,CAAA;IAC5C,CAAC;;AAIH,cAAc,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,CAAA;AACxD,cAAc,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import type { ClientEntrypoint, ClientEntrypointOptions, RefedEntrypointHandler } from './types.js';
2
+ import type { AbstractRequest, CommonEntrypoint } from '@owlmeans/entrypoint';
3
+ import type { JSONSchemaType } from "ajv";
4
+ export declare const elevate: <T = {}, R extends AbstractRequest = AbstractRequest>(entrypoints: (CommonEntrypoint | ClientEntrypoint<T, R>)[], alias: string, handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean, opts?: ClientEntrypointOptions | boolean) => ClientEntrypoint<T, R>[];
5
+ export declare const stab: RefedEntrypointHandler<{}>;
6
+ export declare const provideRequest: <T extends {} = {}>(alias: string, path: string) => AbstractRequest<T>;
7
+ export declare const pickPerSchema: <T, R>(object: T, schema: JSONSchemaType<R>) => Partial<R>;
8
+ //# sourceMappingURL=helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAGnG,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAEzC,eAAO,MAAM,OAAO,GAAI,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EACzE,aAAa,CAAC,gBAAgB,GAAG,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAC1D,OAAO,MAAM,EACb,UAAU,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,uBAAuB,GAAG,OAAO,EAC1E,OAAO,uBAAuB,GAAG,OAAO,KACvC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,EAcxB,CAAA;AAED,eAAO,MAAM,IAAI,EAAE,sBAAsB,CAAC,EAAE,CAE3C,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,eAAe,CAAC,CAAC,CAYhG,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAQjF,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { entrypoint } from './entrypoint.js';
2
+ import { isClientRouteModel } from '@owlmeans/client-route';
3
+ import { normalizeHelperParams } from './utils/entrypoint.js';
4
+ export const elevate = (entrypoints, alias, handler, opts) => {
5
+ [handler, opts] = normalizeHelperParams(handler, opts);
6
+ const idx = entrypoints.findIndex(({ route }) => route.route.alias === alias);
7
+ if (idx === -1) {
8
+ throw new SyntaxError(`Entrypoint with alias ${alias} not present`);
9
+ }
10
+ if (isClientRouteModel(entrypoints[idx].route) && opts?.force !== true) {
11
+ throw new SyntaxError(`Entrypoint with alias ${alias} is already elevated`);
12
+ }
13
+ entrypoints[idx] = entrypoint(entrypoints[idx], handler, opts);
14
+ return entrypoints;
15
+ };
16
+ export const stab = () => () => {
17
+ return void 0;
18
+ };
19
+ export const provideRequest = (alias, path) => {
20
+ const request = {
21
+ alias,
22
+ params: {},
23
+ headers: {},
24
+ query: {},
25
+ path,
26
+ canceled: false,
27
+ cancel: () => request.canceled = true
28
+ };
29
+ return request;
30
+ };
31
+ export const pickPerSchema = (object, schema) => Object.keys(schema.properties).reduce((acc, key) => {
32
+ if (object[key] != null) {
33
+ acc[key] = object[key];
34
+ }
35
+ return acc;
36
+ }, {});
37
+ //# sourceMappingURL=helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,WAA0D,EAC1D,KAAa,EACb,OAA0E,EAC1E,IAAwC,EACd,EAAE;IAC5B,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAEtD,MAAM,GAAG,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;IAC7E,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,WAAW,CAAC,yBAAyB,KAAK,cAAc,CAAC,CAAA;IACrE,CAAC;IACD,IAAI,kBAAkB,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC;QACvE,MAAM,IAAI,WAAW,CAAC,yBAAyB,KAAK,sBAAsB,CAAC,CAAA;IAC7E,CAAC;IAED,WAAW,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAE9D,OAAO,WAAuC,CAAA;AAChD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,IAAI,GAA+B,GAAG,EAAE,CAAC,GAAG,EAAE;IACzD,OAAO,KAAK,CAAQ,CAAA;AACtB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAoB,KAAa,EAAE,IAAY,EAAsB,EAAE;IACnG,MAAM,OAAO,GAAG;QACd,KAAK;QACL,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,IAAI;QACJ,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI;KACtC,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAAO,MAAS,EAAE,MAAyB,EAAc,EAAE,CACtF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CACnC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACX,IAAI,MAAM,CAAC,GAAc,CAAC,IAAI,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,GAAc,CAAC,GAAG,MAAM,CAAC,GAAc,CAA0B,CAAA;IACvE,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,EAAE,EAAgB,CACpB,CAAA"}
@@ -0,0 +1,5 @@
1
+ export * from './helper.js';
2
+ export type * from './types.js';
3
+ export * from './entrypoint.js';
4
+ export * from './errors.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
package/build/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './helper.js';
2
+ export * from './entrypoint.js';
3
+ export * from './errors.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAE3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,33 @@
1
+ import type { ClientRouteModel, ClientRouteOptions } from '@owlmeans/client-route';
2
+ import type { AbstractRequest, AbstractResponse, CommonEntrypoint, CommonEntrypointOptions, EntrypointHandler, EntrypointOutcome } from '@owlmeans/entrypoint';
3
+ export interface ClientEntrypoint<T = {}, R extends ClientRequest = ClientRequest> extends CommonEntrypoint {
4
+ route: ClientRouteModel;
5
+ call: EntrypointCall<T, R>;
6
+ validate: EntrypointFilter<R>;
7
+ getPath: (partial?: boolean) => string;
8
+ request: (request?: Partial<R>) => R;
9
+ }
10
+ export interface EntrypointCall<T, Req extends ClientRequest = ClientRequest> {
11
+ <Type extends T, R extends Req = Req, P extends AbstractResponse<Type> = AbstractResponse<Type>>(req?: Partial<R>, res?: P): Promise<[Type, EntrypointOutcome]>;
12
+ }
13
+ export interface EntrypointFilter<Req extends AbstractRequest = AbstractRequest> {
14
+ <R extends Req>(req?: Partial<R>): Promise<boolean>;
15
+ }
16
+ export interface ClientRequest<T extends {} = {}> extends AbstractRequest<T> {
17
+ full?: boolean;
18
+ }
19
+ export interface ClientEntrypointOptions extends CommonEntrypointOptions {
20
+ /**
21
+ * Force entrypoint to be elevated even if it is already elevated
22
+ */
23
+ force?: boolean;
24
+ routeOptions?: ClientRouteOptions;
25
+ validateOnCall?: boolean;
26
+ }
27
+ export interface EntrypointRef<T, R extends AbstractRequest = AbstractRequest> {
28
+ ref?: ClientEntrypoint<T, R>;
29
+ }
30
+ export interface RefedEntrypointHandler<T, R extends AbstractRequest = AbstractRequest> {
31
+ (ref: EntrypointRef<T, R>): EntrypointHandler;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAE9J,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,aAAa,GAAG,aAAa,CAAE,SAAQ,gBAAgB;IACzG,KAAK,EAAE,gBAAgB,CAAA;IACvB,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC7B,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,MAAM,CAAA;IACtC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,EAAE,GAAG,SAAS,aAAa,GAAG,aAAa;IAC1E,CACE,IAAI,SAAS,CAAC,EAAE,CAAC,SAAS,GAAG,GAAG,GAAG,EAAE,CAAC,SAAS,gBAAgB,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAC9F,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAA;CACjE;AAED,MAAM,WAAW,gBAAgB,CAAC,GAAG,SAAS,eAAe,GAAG,eAAe;IAC7E,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACpD;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,CAAE,SAAQ,eAAe,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe;IAC3E,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe;IACpF,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,iBAAiB,CAAA;CAC9C"}
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { EntrypointFilter, ClientEntrypointOptions } from '../types.js';
2
+ import type { AbstractRequest } from '@owlmeans/entrypoint';
3
+ import type { EntrypointRef, RefedEntrypointHandler } from '../types.js';
4
+ export { entrypoint as makeBasicEntrypoint } from '@owlmeans/entrypoint';
5
+ export { isEntrypoint } from '@owlmeans/entrypoint/utils';
6
+ /**
7
+ * @throws {SyntaxError} if data shape doesn't match validation
8
+ * @throws {ClientValidationError} if request is not valid
9
+ */
10
+ export declare const validate: <T, R extends AbstractRequest = AbstractRequest>(ref: EntrypointRef<T, R>) => EntrypointFilter;
11
+ export declare const normalizeHelperParams: <T, R extends AbstractRequest = AbstractRequest>(handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean, opts?: ClientEntrypointOptions | boolean) => [RefedEntrypointHandler<T, R> | undefined, ClientEntrypointOptions | undefined];
12
+ //# sourceMappingURL=entrypoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../../src/utils/entrypoint.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGxE,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAKzD;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,gBAiClG,CAAA;AAEH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EAClF,UAAU,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,uBAAuB,GAAG,OAAO,EAC1E,OAAO,uBAAuB,GAAG,OAAO,KACvC,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,uBAAuB,GAAG,SAAS,CAShF,CAAA"}
@@ -0,0 +1,51 @@
1
+ import Ajv from 'ajv';
2
+ import { ClientValidationError } from '../errors.js';
3
+ import formatsPlugin from 'ajv-formats';
4
+ export { entrypoint as makeBasicEntrypoint } from '@owlmeans/entrypoint';
5
+ export { isEntrypoint } from '@owlmeans/entrypoint/utils';
6
+ const ajv = new Ajv({ strict: false });
7
+ formatsPlugin(ajv);
8
+ /**
9
+ * @throws {SyntaxError} if data shape doesn't match validation
10
+ * @throws {ClientValidationError} if request is not valid
11
+ */
12
+ export const validate = (ref) => async (req) => {
13
+ const ep = ref.ref;
14
+ if (ep == null) {
15
+ throw new SyntaxError('Try to make API call before without entrypoint');
16
+ }
17
+ if (req?.alias == null) {
18
+ throw new SyntaxError(`Can't validate for unknown entrypoint (request.alias required)`);
19
+ }
20
+ if (ep.filter == null) {
21
+ throw new SyntaxError(`Entrypoint ${req.alias} has no filter to validate against`);
22
+ }
23
+ const results = await Promise.all(Object.entries(ep.filter).filter(([key]) => !['headers', 'response'].includes(key))
24
+ .map(async ([key, filter]) => {
25
+ if (req[key] == null) {
26
+ throw new SyntaxError(`Request has no required section ${key}`);
27
+ }
28
+ const validateFn = ajv.compile(filter);
29
+ validateFn(req[key]);
30
+ if (validateFn.errors == null) {
31
+ return true;
32
+ }
33
+ else {
34
+ return validateFn.errors;
35
+ }
36
+ }));
37
+ if (results.some((result) => result !== true)) {
38
+ const errors = results.find(result => result !== true && result != null);
39
+ throw new ClientValidationError(`${errors[0].instancePath}|${errors[0].message}`);
40
+ }
41
+ return true;
42
+ };
43
+ export const normalizeHelperParams = (handler, opts) => {
44
+ if (typeof handler !== 'function' && handler != null) {
45
+ opts = handler;
46
+ handler = undefined;
47
+ }
48
+ opts = typeof opts === 'boolean' ? { validateOnCall: opts } : opts;
49
+ return [handler, opts];
50
+ };
51
+ //# sourceMappingURL=entrypoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.js","sourceRoot":"","sources":["../../src/utils/entrypoint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { AbstractRequest, EntrypointHandler } from '@owlmeans/entrypoint';
2
+ import type { EntrypointCall, ClientEntrypointOptions, EntrypointRef, ClientRequest } from '../types.js';
3
+ export declare const apiHandler: <T, R extends AbstractRequest = AbstractRequest>(ref: EntrypointRef<T, R>) => EntrypointHandler;
4
+ export declare const apiCall: <T, R extends AbstractRequest = AbstractRequest>(ref: EntrypointRef<T, R>, opts?: ClientEntrypointOptions) => EntrypointCall<T, R>;
5
+ export declare const urlCall: <T, R extends ClientRequest = ClientRequest>(ref: EntrypointRef<T, R>, opts?: ClientEntrypointOptions) => EntrypointCall<T, R>;
6
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAE9E,OAAO,KAAK,EAAoB,cAAc,EAAE,uBAAuB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAW1H,eAAO,MAAM,UAAU,EAAE,CACvB,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EAC9C,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,iBA0B9B,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,CACpB,CAAC,EAAE,CAAC,SAAS,eAAe,GAAG,eAAe,EAC9C,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,uBAAuB,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAuDvD,CAAA;AAE3B,eAAO,MAAM,OAAO,EAAE,CACpB,CAAC,EAAE,CAAC,SAAS,aAAa,GAAG,aAAa,EAC1C,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,uBAAuB,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CA4BjF,CAAA"}
@@ -0,0 +1,104 @@
1
+ import { DEFAULT_KEY } from '@owlmeans/client-config';
2
+ import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint';
3
+ import { validate } from './entrypoint.js';
4
+ import { extractParams } from '@owlmeans/client-route';
5
+ import { PARAM } from '@owlmeans/route';
6
+ import { stringify } from 'qs';
7
+ import { assertContext } from '@owlmeans/context';
8
+ import { makeSecurityHelper } from '@owlmeans/config';
9
+ export const apiHandler = (ref) => async (req, res) => {
10
+ const location = `client-entrypoint:api-handler:${ref.ref?.alias}`;
11
+ const context = assertContext(ref.ref?.ctx, location);
12
+ if (context.cfg.webService == null) {
13
+ throw new SyntaxError('No webService provided');
14
+ }
15
+ const ep = context.entrypoint(req.alias);
16
+ const route = await ep.route.resolve(context);
17
+ let alias = typeof context.cfg.webService === 'string'
18
+ ? context.cfg.webService
19
+ : (route.service != null
20
+ ? context.cfg.webService[route.service] ?? context.cfg.webService[DEFAULT_KEY]
21
+ : context.cfg.webService[DEFAULT_KEY]);
22
+ if (alias == null) {
23
+ throw new SyntaxError(`Can't cast web service alias for ${ep.alias} entrypoint`);
24
+ }
25
+ const service = context.service(alias);
26
+ req.path = ep.getPath();
27
+ return service.handler(req, res);
28
+ };
29
+ export const apiCall = (ref, opts) => (async (req, res) => {
30
+ const ep = ref.ref;
31
+ if (ep == null) {
32
+ throw new SyntaxError('Try to make API call before the entrypoint is created');
33
+ }
34
+ const ctx = ep.ctx;
35
+ if (ctx == null) {
36
+ throw new SyntaxError(`No context provided in apiCall for ${ep.alias} entrypoint`);
37
+ }
38
+ await ep.route.resolve(ctx);
39
+ if (req?.canceled) {
40
+ return;
41
+ }
42
+ const request = {
43
+ alias: ep.alias,
44
+ params: req?.params ?? {},
45
+ body: req?.body,
46
+ headers: req?.headers ?? {},
47
+ query: req?.query ?? {},
48
+ host: req?.host,
49
+ base: req?.base,
50
+ path: ep.getPath(),
51
+ };
52
+ if (req?.cancel != null) {
53
+ const cancel = req.cancel;
54
+ req.cancel = () => {
55
+ cancel();
56
+ request.canceled = true;
57
+ };
58
+ }
59
+ if (opts?.validateOnCall) {
60
+ try {
61
+ await validate(ref)(request);
62
+ }
63
+ catch (e) {
64
+ console.error(e);
65
+ throw e;
66
+ }
67
+ }
68
+ if (res != null) {
69
+ await apiHandler(ref)(request, res);
70
+ return;
71
+ }
72
+ const reply = provideResponse();
73
+ if (ctx == null && ep.ctx == null) {
74
+ throw new SyntaxError(`Use entrypoint ${ep.alias} without context`);
75
+ }
76
+ await apiHandler(ref)(request, reply);
77
+ if (reply.error != null) {
78
+ throw reply.error;
79
+ }
80
+ return [reply.value ?? null, reply.outcome ?? EntrypointOutcome.Ok];
81
+ });
82
+ export const urlCall = ref => async (req, res) => {
83
+ const ep = ref.ref;
84
+ if (ep == null) {
85
+ throw new SyntaxError('Try to make URL before the entrypoint is created');
86
+ }
87
+ const ctx = ep.ctx;
88
+ if (ctx == null) {
89
+ throw new SyntaxError(`No context provided in urlCall for ${ep.alias} entrypoint`);
90
+ }
91
+ await ep.route.resolve(ctx);
92
+ const pathParams = extractParams(ep.getPath());
93
+ let path = pathParams.reduce((p, param) => {
94
+ return p.replace(`${PARAM}${param}`, `${req?.params?.[param]}`);
95
+ }, ep.getPath()) + (req?.query != null ? `?${stringify(req?.query)}` : '');
96
+ if (ep.route.route.service !== null && (ctx.cfg.service !== ep.route.route.service
97
+ || req?.full === true)) {
98
+ const helper = makeSecurityHelper(ctx);
99
+ path = helper.makeUrl(ep.route.route, path, { host: req?.host, base: req?.base, forceUnsecure: req?.unsecure });
100
+ }
101
+ res?.resolve(path, EntrypointOutcome.Ok);
102
+ return [path, EntrypointOutcome.Ok];
103
+ };
104
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/utils/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from './entrypoint.js';
2
+ export * from './handler.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './entrypoint.js';
2
+ export * from './handler.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@owlmeans/client-entrypoint",
3
+ "version": "0.1.3",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc -b",
8
+ "dev": "sleep 102 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
9
+ "watch": "tsc -b -w --preserveWatchOutput --pretty"
10
+ },
11
+ "main": "build/index.js",
12
+ "module": "build/index.js",
13
+ "types": "build/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": "./build/index.js",
17
+ "require": "./build/index.js",
18
+ "default": "./build/index.js",
19
+ "module": "./build/index.js",
20
+ "types": "./build/index.d.ts"
21
+ },
22
+ "./utils": {
23
+ "import": "./build/utils/index.js",
24
+ "require": "./build/utils/index.js",
25
+ "default": "./build/utils/index.js",
26
+ "module": "./build/utils/index.js",
27
+ "types": "./build/utils/index.d.ts"
28
+ }
29
+ },
30
+ "dependencies": {
31
+ "@owlmeans/api": "^0.1.3",
32
+ "@owlmeans/client-config": "^0.1.3",
33
+ "@owlmeans/client-context": "^0.1.3",
34
+ "@owlmeans/client-route": "^0.1.3",
35
+ "@owlmeans/config": "^0.1.3",
36
+ "@owlmeans/context": "^0.1.3",
37
+ "@owlmeans/entrypoint": "^0.1.3",
38
+ "@owlmeans/error": "^0.1.3",
39
+ "@owlmeans/route": "^0.1.3",
40
+ "ajv-formats": "^3.0.1",
41
+ "qs": "^6.13.0"
42
+ },
43
+ "peerDependencies": {
44
+ "ajv": "*"
45
+ },
46
+ "devDependencies": {
47
+ "@owlmeans/dep-config": "workspace:*",
48
+ "@types/qs": "^6.9.16",
49
+ "nodemon": "^3.1.11",
50
+ "npm-check": "^6.0.1",
51
+ "typescript": "^6.0.2"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }
@@ -0,0 +1,88 @@
1
+ import type { ClientRouteModel } from '@owlmeans/client-route'
2
+ import type { ClientEntrypoint, ClientEntrypointOptions, EntrypointRef, RefedEntrypointHandler } from './types.js'
3
+ import type { AbstractRequest, CommonEntrypoint } from '@owlmeans/entrypoint'
4
+ import { isEntrypoint, makeBasicEntrypoint, normalizeHelperParams, validate } from './utils/entrypoint.js'
5
+ import { isClientRouteModel, route } from '@owlmeans/client-route'
6
+ import { apiCall, apiHandler, urlCall } from './utils/handler.js'
7
+ import { AppType } from '@owlmeans/context'
8
+ import type { BasicContext } from '@owlmeans/context'
9
+ import { normalizePath } from '@owlmeans/route'
10
+ import type { CommonRouteModel } from '@owlmeans/route'
11
+ import { provideRequest } from './helper.js'
12
+
13
+ export const entrypoint = <T, R extends AbstractRequest = AbstractRequest>(
14
+ arg: CommonEntrypoint | ClientRouteModel | CommonRouteModel,
15
+ handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean,
16
+ opts?: ClientEntrypointOptions | boolean
17
+ ): ClientEntrypoint<T, R> => {
18
+ const entrypointHandle: EntrypointRef<T, R> = { ref: undefined }
19
+
20
+ let _entrypoint: ClientEntrypoint<T, R>
21
+
22
+ ;[handler, opts] = normalizeHelperParams(handler, opts)
23
+
24
+ const _handler = handler as RefedEntrypointHandler<T, R> | undefined ??
25
+ (('route' in arg.route ? arg.route.route.type : arg.route.type)
26
+ === AppType.Backend ? apiHandler : undefined)
27
+
28
+ if (isEntrypoint(arg)) {
29
+ assertExplicitHandler(arg.route.route.type, handler as RefedEntrypointHandler<T, R>)
30
+ const routeModel = route(arg.route, opts?.routeOptions)
31
+ _entrypoint = arg as ClientEntrypoint<T, R>
32
+ _entrypoint.route = routeModel
33
+ _entrypoint.guards = opts?.guards ?? arg.guards
34
+ _entrypoint.filter = opts?.filter ?? arg.filter
35
+ _entrypoint.gate = opts?.gate ?? arg.gate
36
+ _entrypoint.gateParams = opts?.gateParams ?? arg.gateParams
37
+ } else if (isClientRouteModel(arg)) {
38
+ assertExplicitHandler(arg.route.type, handler as RefedEntrypointHandler<T, R>)
39
+ _entrypoint = makeBasicEntrypoint(arg, { ...opts }) as ClientEntrypoint<T, R>
40
+ _entrypoint.route = arg
41
+ } else {
42
+ assertExplicitHandler(arg.route.type, handler as RefedEntrypointHandler<T, R>)
43
+ const _route = route(arg, opts?.routeOptions)
44
+ _entrypoint = makeBasicEntrypoint(_route, { ...opts }) as ClientEntrypoint<T, R>
45
+ _entrypoint.route = _route
46
+ }
47
+
48
+ _entrypoint.getPath = (partial?: boolean) =>
49
+ partial === true ? normalizePath(_entrypoint.route.route.partialPath) : _entrypoint.route.route.path
50
+
51
+ _entrypoint.call = handler != null ? urlCall<T, R>(entrypointHandle, opts) : apiCall<T, R>(entrypointHandle, opts)
52
+
53
+ _entrypoint.request = ((request: R): R => {
54
+ if (entrypointHandle.ref == null) {
55
+ throw SyntaxError(`Try to request uninitialized entrypoint ${JSON.stringify(arg)}`)
56
+ }
57
+ const _request = provideRequest(entrypointHandle.ref.getAlias(), entrypointHandle.ref.getPath()) as R
58
+
59
+ request != null && Object.entries(request).forEach(([key, value]) => {
60
+ _request[key as keyof R] = value
61
+ })
62
+
63
+ return _request
64
+ }) as any
65
+
66
+ _entrypoint.handle = _handler?.(entrypointHandle)
67
+
68
+ _entrypoint.validate = validate(entrypointHandle)
69
+
70
+ _entrypoint.reinitializeContext = <T>(context: BasicContext<any>) => {
71
+ const newEntrypoint = entrypoint(_entrypoint.route, handler, opts)
72
+ newEntrypoint.ctx = context
73
+
74
+ return newEntrypoint as T
75
+ }
76
+
77
+ entrypointHandle.ref = _entrypoint
78
+
79
+ return _entrypoint
80
+ }
81
+
82
+ const assertExplicitHandler = <T, R extends AbstractRequest = AbstractRequest>(
83
+ type: AppType, handler: RefedEntrypointHandler<T, R> | undefined
84
+ ) => {
85
+ if (type === AppType.Backend && handler != null) {
86
+ throw new SyntaxError('We can\'t provide explicit handler to backend client entrypoint')
87
+ }
88
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,23 @@
1
+
2
+ import { ResilientError } from '@owlmeans/error'
3
+
4
+ export class ClientEntrypointError extends ResilientError {
5
+ public static override typeName = 'ClientModuleError'
6
+
7
+ constructor(message: string) {
8
+ super(ClientEntrypointError.typeName, `clinet-module:${message}`)
9
+ }
10
+ }
11
+
12
+ export class ClientValidationError extends ClientEntrypointError {
13
+ public static override typeName = `${ClientEntrypointError.typeName}Validation`
14
+
15
+ constructor(message: string) {
16
+ super(`validation:${message}`)
17
+ this.type = ClientValidationError.typeName
18
+ }
19
+ }
20
+
21
+
22
+ ResilientError.registerErrorClass(ClientEntrypointError)
23
+ ResilientError.registerErrorClass(ClientValidationError)
package/src/helper.ts ADDED
@@ -0,0 +1,55 @@
1
+ import type { ClientEntrypoint, ClientEntrypointOptions, RefedEntrypointHandler } from './types.js'
2
+ import { entrypoint } from './entrypoint.js'
3
+ import { isClientRouteModel } from '@owlmeans/client-route'
4
+ import type { AbstractRequest, CommonEntrypoint } from '@owlmeans/entrypoint'
5
+ import { normalizeHelperParams } from './utils/entrypoint.js'
6
+ import type { JSONSchemaType } from "ajv"
7
+
8
+ export const elevate = <T = {}, R extends AbstractRequest = AbstractRequest>(
9
+ entrypoints: (CommonEntrypoint | ClientEntrypoint<T, R>)[],
10
+ alias: string,
11
+ handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean,
12
+ opts?: ClientEntrypointOptions | boolean
13
+ ): ClientEntrypoint<T, R>[] => {
14
+ [handler, opts] = normalizeHelperParams(handler, opts)
15
+
16
+ const idx = entrypoints.findIndex(({ route }) => route.route.alias === alias)
17
+ if (idx === -1) {
18
+ throw new SyntaxError(`Entrypoint with alias ${alias} not present`)
19
+ }
20
+ if (isClientRouteModel(entrypoints[idx].route) && opts?.force !== true) {
21
+ throw new SyntaxError(`Entrypoint with alias ${alias} is already elevated`)
22
+ }
23
+
24
+ entrypoints[idx] = entrypoint(entrypoints[idx], handler, opts)
25
+
26
+ return entrypoints as ClientEntrypoint<T, R>[]
27
+ }
28
+
29
+ export const stab: RefedEntrypointHandler<{}> = () => () => {
30
+ return void 0 as any
31
+ }
32
+
33
+ export const provideRequest = <T extends {} = {}>(alias: string, path: string): AbstractRequest<T> => {
34
+ const request = {
35
+ alias,
36
+ params: {},
37
+ headers: {},
38
+ query: {},
39
+ path,
40
+ canceled: false,
41
+ cancel: () => request.canceled = true
42
+ }
43
+
44
+ return request
45
+ }
46
+
47
+ export const pickPerSchema = <T, R>(object: T, schema: JSONSchemaType<R>): Partial<R> =>
48
+ Object.keys(schema.properties).reduce(
49
+ (acc, key) => {
50
+ if (object[key as keyof T] != null) {
51
+ acc[key as keyof R] = object[key as keyof T] as unknown as R[keyof R]
52
+ }
53
+ return acc
54
+ }, {} as Partial<R>
55
+ )
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+
2
+ export * from './helper.js'
3
+ export type * from './types.js'
4
+ export * from './entrypoint.js'
5
+ export * from './errors.js'
package/src/types.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { ClientRouteModel, ClientRouteOptions } from '@owlmeans/client-route'
2
+ import type { AbstractRequest, AbstractResponse, CommonEntrypoint, CommonEntrypointOptions, EntrypointHandler, EntrypointOutcome } from '@owlmeans/entrypoint'
3
+
4
+ export interface ClientEntrypoint<T = {}, R extends ClientRequest = ClientRequest> extends CommonEntrypoint {
5
+ route: ClientRouteModel
6
+ call: EntrypointCall<T, R>
7
+ validate: EntrypointFilter<R>
8
+ getPath: (partial?: boolean) => string
9
+ request: (request?: Partial<R>) => R
10
+ }
11
+
12
+ export interface EntrypointCall<T, Req extends ClientRequest = ClientRequest> {
13
+ <
14
+ Type extends T, R extends Req = Req, P extends AbstractResponse<Type> = AbstractResponse<Type>
15
+ >(req?: Partial<R>, res?: P): Promise<[Type, EntrypointOutcome]>
16
+ }
17
+
18
+ export interface EntrypointFilter<Req extends AbstractRequest = AbstractRequest> {
19
+ <R extends Req>(req?: Partial<R>): Promise<boolean>
20
+ }
21
+
22
+ export interface ClientRequest<T extends {} = {}> extends AbstractRequest<T> {
23
+ full?: boolean
24
+ }
25
+
26
+ export interface ClientEntrypointOptions extends CommonEntrypointOptions {
27
+ /**
28
+ * Force entrypoint to be elevated even if it is already elevated
29
+ */
30
+ force?: boolean
31
+ routeOptions?: ClientRouteOptions
32
+ validateOnCall?: boolean
33
+ }
34
+
35
+ export interface EntrypointRef<T, R extends AbstractRequest = AbstractRequest> {
36
+ ref?: ClientEntrypoint<T, R>
37
+ }
38
+
39
+ export interface RefedEntrypointHandler<T, R extends AbstractRequest = AbstractRequest> {
40
+ (ref: EntrypointRef<T, R>): EntrypointHandler
41
+ }
@@ -0,0 +1,66 @@
1
+ import Ajv from 'ajv'
2
+ import type { ErrorObject } from 'ajv'
3
+ import type { EntrypointFilter, ClientEntrypointOptions } from '../types.js'
4
+ import { ClientValidationError } from '../errors.js'
5
+ import type { AbstractRequest } from '@owlmeans/entrypoint'
6
+ import type { EntrypointRef, RefedEntrypointHandler } from '../types.js'
7
+ import formatsPlugin from 'ajv-formats'
8
+
9
+ export { entrypoint as makeBasicEntrypoint } from '@owlmeans/entrypoint'
10
+ export { isEntrypoint } from '@owlmeans/entrypoint/utils'
11
+
12
+ const ajv = new Ajv({ strict: false })
13
+ formatsPlugin(ajv as any)
14
+
15
+ /**
16
+ * @throws {SyntaxError} if data shape doesn't match validation
17
+ * @throws {ClientValidationError} if request is not valid
18
+ */
19
+ export const validate: <T, R extends AbstractRequest = AbstractRequest>(ref: EntrypointRef<T, R>) => EntrypointFilter =
20
+ (ref) => async (req) => {
21
+ const ep = ref.ref
22
+ if (ep == null) {
23
+ throw new SyntaxError('Try to make API call before without entrypoint')
24
+ }
25
+ if (req?.alias == null) {
26
+ throw new SyntaxError(`Can't validate for unknown entrypoint (request.alias required)`)
27
+ }
28
+ if (ep.filter == null) {
29
+ throw new SyntaxError(`Entrypoint ${req.alias} has no filter to validate against`)
30
+ }
31
+ const results = await Promise.all(Object.entries(ep.filter).filter(([key]) => !['headers', 'response'].includes(key))
32
+ .map(async ([key, filter]) => {
33
+ if (req[key as keyof typeof req] == null) {
34
+ throw new SyntaxError(`Request has no required section ${key}`)
35
+ }
36
+
37
+ const validateFn = ajv.compile(filter)
38
+ validateFn(req[key as keyof typeof req])
39
+ if (validateFn.errors == null) {
40
+ return true
41
+ } else {
42
+ return validateFn.errors
43
+ }
44
+ }))
45
+ if (results.some((result) => result !== true)) {
46
+ const errors = results.find(result => result !== true && result != null)! as ErrorObject[]
47
+
48
+ throw new ClientValidationError(`${errors[0].instancePath}|${errors[0].message}`)
49
+ }
50
+
51
+ return true
52
+ }
53
+
54
+ export const normalizeHelperParams = <T, R extends AbstractRequest = AbstractRequest>(
55
+ handler?: RefedEntrypointHandler<T, R> | ClientEntrypointOptions | boolean,
56
+ opts?: ClientEntrypointOptions | boolean
57
+ ): [RefedEntrypointHandler<T, R> | undefined, ClientEntrypointOptions | undefined] => {
58
+ if (typeof handler !== 'function' && handler != null) {
59
+ opts = handler as ClientEntrypointOptions | boolean
60
+ handler = undefined
61
+ }
62
+
63
+ opts = typeof opts === 'boolean' ? { validateOnCall: opts } : opts
64
+
65
+ return [handler, opts]
66
+ }
@@ -0,0 +1,136 @@
1
+ import type { ApiClient } from '@owlmeans/api'
2
+ import { DEFAULT_KEY } from '@owlmeans/client-config'
3
+ import type { ClientConfig, ClientContext } from '@owlmeans/client-context'
4
+ import type { AbstractRequest, EntrypointHandler } from '@owlmeans/entrypoint'
5
+ import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint'
6
+ import type { ClientEntrypoint, EntrypointCall, ClientEntrypointOptions, EntrypointRef, ClientRequest } from '../types.js'
7
+ import { validate } from './entrypoint.js'
8
+ import { extractParams } from '@owlmeans/client-route'
9
+ import { PARAM } from '@owlmeans/route'
10
+ import { stringify } from 'qs'
11
+ import { assertContext } from '@owlmeans/context'
12
+ import { makeSecurityHelper } from '@owlmeans/config'
13
+
14
+ type Config = ClientConfig
15
+ interface Context<C extends Config = Config> extends ClientContext<C> { }
16
+
17
+ export const apiHandler: <
18
+ T, R extends AbstractRequest = AbstractRequest
19
+ >(ref: EntrypointRef<T, R>) => EntrypointHandler = (ref) => async (req, res) => {
20
+ const location = `client-entrypoint:api-handler:${ref.ref?.alias}`
21
+ const context = assertContext<Config, Context>(ref.ref?.ctx as Context, location)
22
+
23
+ if (context.cfg.webService == null) {
24
+ throw new SyntaxError('No webService provided')
25
+ }
26
+
27
+ const ep = context.entrypoint<ClientEntrypoint>(req.alias)
28
+ const route = await ep.route.resolve<Config, Context>(context)
29
+
30
+ let alias: string | undefined = typeof context.cfg.webService === 'string'
31
+ ? context.cfg.webService
32
+ : (route.service != null
33
+ ? context.cfg.webService[route.service] ?? context.cfg.webService[DEFAULT_KEY]
34
+ : context.cfg.webService[DEFAULT_KEY])
35
+
36
+ if (alias == null) {
37
+ throw new SyntaxError(`Can't cast web service alias for ${ep.alias} entrypoint`)
38
+ }
39
+
40
+ const service: ApiClient = context.service(alias)
41
+
42
+ req.path = ep.getPath()
43
+
44
+ return service.handler(req, res as any)
45
+ }
46
+
47
+ export const apiCall: <
48
+ T, R extends AbstractRequest = AbstractRequest
49
+ >(ref: EntrypointRef<T, R>, opts?: ClientEntrypointOptions) => EntrypointCall<T, R> =
50
+ (ref, opts) => (async (req, res) => {
51
+ const ep = ref.ref
52
+ if (ep == null) {
53
+ throw new SyntaxError('Try to make API call before the entrypoint is created')
54
+ }
55
+ const ctx = ep.ctx
56
+ if (ctx == null) {
57
+ throw new SyntaxError(`No context provided in apiCall for ${ep.alias} entrypoint`)
58
+ }
59
+ await ep.route.resolve(ctx)
60
+
61
+ if (req?.canceled) {
62
+ return
63
+ }
64
+
65
+ const request: AbstractRequest = {
66
+ alias: ep.alias,
67
+ params: req?.params ?? {},
68
+ body: req?.body,
69
+ headers: req?.headers ?? {},
70
+ query: req?.query ?? {},
71
+ host: req?.host,
72
+ base: req?.base,
73
+ path: ep.getPath(),
74
+ }
75
+ if (req?.cancel != null) {
76
+ const cancel = req.cancel
77
+ req.cancel = () => {
78
+ cancel()
79
+ request.canceled = true
80
+ }
81
+ }
82
+ if (opts?.validateOnCall) {
83
+ try {
84
+ await validate(ref)(request)
85
+ } catch (e) {
86
+ console.error(e)
87
+ throw e
88
+ }
89
+ }
90
+ if (res != null) {
91
+ await apiHandler(ref)(request, res)
92
+ return
93
+ }
94
+ const reply = provideResponse<unknown>()
95
+ if (ctx == null && ep.ctx == null) {
96
+ throw new SyntaxError(`Use entrypoint ${ep.alias} without context`)
97
+ }
98
+ await apiHandler(ref)(request, reply)
99
+ if (reply.error != null) {
100
+ throw reply.error
101
+ }
102
+
103
+ return [reply.value ?? null, reply.outcome ?? EntrypointOutcome.Ok]
104
+ }) as EntrypointCall<any>
105
+
106
+ export const urlCall: <
107
+ T, R extends ClientRequest = ClientRequest
108
+ >(ref: EntrypointRef<T, R>, opts?: ClientEntrypointOptions) => EntrypointCall<T, R> = ref => async (req, res) => {
109
+ const ep = ref.ref
110
+ if (ep == null) {
111
+ throw new SyntaxError('Try to make URL before the entrypoint is created')
112
+ }
113
+ const ctx = ep.ctx
114
+ if (ctx == null) {
115
+ throw new SyntaxError(`No context provided in urlCall for ${ep.alias} entrypoint`)
116
+ }
117
+ await ep.route.resolve(ctx)
118
+
119
+ const pathParams = extractParams(ep.getPath())
120
+ let path = pathParams.reduce((p, param) => {
121
+ return p.replace(`${PARAM}${param}`, `${req?.params?.[param as keyof typeof req.params]}`)
122
+ }, ep.getPath()) + (req?.query != null ? `?${stringify(req?.query)}` : '')
123
+
124
+ if (ep.route.route.service !== null && (
125
+ ctx.cfg.service !== ep.route.route.service
126
+ || req?.full === true)) {
127
+ const helper = makeSecurityHelper(ctx)
128
+ path = helper.makeUrl(
129
+ ep.route.route, path, { host: req?.host, base: req?.base, forceUnsecure: req?.unsecure }
130
+ )
131
+ }
132
+
133
+ res?.resolve(path as any, EntrypointOutcome.Ok)
134
+
135
+ return [path as any, EntrypointOutcome.Ok]
136
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './entrypoint.js'
3
+ export * from './handler.js'
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": [
3
+ "@owlmeans/dep-config/tsconfig.base.json"
4
+ ],
5
+ "compilerOptions": {
6
+ "rootDir": "./src/",
7
+ "outDir": "./build/"
8
+ },
9
+ "exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
10
+ }