@simplysm/service-server 13.0.100 → 14.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +79 -133
  2. package/dist/auth/auth-token-payload.js +2 -1
  3. package/dist/auth/auth-token-payload.js.map +1 -6
  4. package/dist/auth/jwt-manager.js +21 -21
  5. package/dist/auth/jwt-manager.js.map +1 -6
  6. package/dist/core/define-service.d.ts +12 -12
  7. package/dist/core/define-service.d.ts.map +1 -1
  8. package/dist/core/define-service.js +77 -63
  9. package/dist/core/define-service.js.map +1 -6
  10. package/dist/core/service-executor.d.ts.map +1 -1
  11. package/dist/core/service-executor.js +42 -32
  12. package/dist/core/service-executor.js.map +1 -6
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +11 -2
  16. package/dist/index.js.map +1 -6
  17. package/dist/legacy/v1-auto-update-handler.d.ts +2 -2
  18. package/dist/legacy/v1-auto-update-handler.js +42 -35
  19. package/dist/legacy/v1-auto-update-handler.js.map +1 -6
  20. package/dist/protocol/protocol-wrapper.d.ts +9 -9
  21. package/dist/protocol/protocol-wrapper.js +64 -46
  22. package/dist/protocol/protocol-wrapper.js.map +1 -6
  23. package/dist/service-server.d.ts +2 -1
  24. package/dist/service-server.d.ts.map +1 -1
  25. package/dist/service-server.js +183 -165
  26. package/dist/service-server.js.map +1 -6
  27. package/dist/services/auto-update-service.js +35 -34
  28. package/dist/services/auto-update-service.js.map +1 -6
  29. package/dist/services/orm-service.js +114 -120
  30. package/dist/services/orm-service.js.map +1 -6
  31. package/dist/transport/http/http-request-handler.d.ts.map +1 -1
  32. package/dist/transport/http/http-request-handler.js +58 -46
  33. package/dist/transport/http/http-request-handler.js.map +1 -6
  34. package/dist/transport/http/static-file-handler.js +42 -39
  35. package/dist/transport/http/static-file-handler.js.map +1 -6
  36. package/dist/transport/http/upload-handler.d.ts.map +1 -1
  37. package/dist/transport/http/upload-handler.js +60 -55
  38. package/dist/transport/http/upload-handler.js.map +1 -6
  39. package/dist/transport/socket/service-socket.d.ts +13 -13
  40. package/dist/transport/socket/service-socket.js +132 -108
  41. package/dist/transport/socket/service-socket.js.map +1 -6
  42. package/dist/transport/socket/websocket-handler.d.ts +9 -13
  43. package/dist/transport/socket/websocket-handler.d.ts.map +1 -1
  44. package/dist/transport/socket/websocket-handler.js +148 -139
  45. package/dist/transport/socket/websocket-handler.js.map +1 -6
  46. package/dist/types/server-options.d.ts +1 -1
  47. package/dist/types/server-options.d.ts.map +1 -1
  48. package/dist/types/server-options.js +2 -1
  49. package/dist/types/server-options.js.map +1 -6
  50. package/dist/utils/config-manager.js +48 -46
  51. package/dist/utils/config-manager.js.map +1 -6
  52. package/dist/workers/service-protocol.worker.js +8 -11
  53. package/dist/workers/service-protocol.worker.js.map +1 -6
  54. package/docs/auth.md +28 -16
  55. package/docs/core.md +113 -54
  56. package/docs/legacy.md +21 -0
  57. package/docs/main.md +81 -0
  58. package/docs/protocol.md +31 -0
  59. package/docs/services.md +49 -44
  60. package/docs/transport.md +81 -76
  61. package/docs/types.md +31 -0
  62. package/docs/utilities.md +27 -0
  63. package/package.json +12 -13
  64. package/src/auth/jwt-manager.ts +2 -2
  65. package/src/core/define-service.ts +19 -19
  66. package/src/core/service-executor.ts +23 -17
  67. package/src/index.ts +10 -12
  68. package/src/legacy/v1-auto-update-handler.ts +10 -10
  69. package/src/protocol/protocol-wrapper.ts +16 -16
  70. package/src/service-server.ts +51 -43
  71. package/src/services/auto-update-service.ts +1 -1
  72. package/src/services/orm-service.ts +7 -7
  73. package/src/transport/http/http-request-handler.ts +16 -10
  74. package/src/transport/http/static-file-handler.ts +8 -8
  75. package/src/transport/http/upload-handler.ts +16 -9
  76. package/src/transport/socket/service-socket.ts +22 -22
  77. package/src/transport/socket/websocket-handler.ts +50 -70
  78. package/src/types/server-options.ts +1 -1
  79. package/src/utils/config-manager.ts +11 -11
  80. package/dist/services/smtp-client-service.d.ts +0 -8
  81. package/dist/services/smtp-client-service.d.ts.map +0 -1
  82. package/dist/services/smtp-client-service.js +0 -46
  83. package/dist/services/smtp-client-service.js.map +0 -6
  84. package/docs/server.md +0 -126
  85. package/src/services/smtp-client-service.ts +0 -59
  86. package/tests/define-service.spec.ts +0 -66
  87. package/tests/orm-service.spec.ts +0 -83
  88. package/tests/service-executor.spec.ts +0 -114
@@ -1,59 +0,0 @@
1
- import nodemailer from "nodemailer";
2
- import { defineService, type ServiceMethods } from "../core/define-service";
3
- import type {
4
- SmtpClientDefaultOptions,
5
- SmtpClientSendByDefaultOption,
6
- SmtpClientSendOption,
7
- } from "@simplysm/service-common";
8
-
9
- export const SmtpClientService = defineService("SmtpClient", (ctx) => ({
10
- async send(options: SmtpClientSendOption): Promise<string> {
11
- return new Promise<string>((resolve, reject) => {
12
- const transport = nodemailer.createTransport({
13
- host: options.host,
14
- port: options.port,
15
- secure: options.secure,
16
- auth:
17
- options.user != null
18
- ? {
19
- user: options.user,
20
- pass: options.pass,
21
- }
22
- : undefined,
23
- tls: {
24
- rejectUnauthorized: false,
25
- },
26
- });
27
-
28
- transport.sendMail(options as nodemailer.SendMailOptions, (err, info) => {
29
- if (err) {
30
- reject(err);
31
- return;
32
- }
33
-
34
- resolve(info.messageId);
35
- });
36
- });
37
- },
38
-
39
- async sendByConfig(configName: string, options: SmtpClientSendByDefaultOption): Promise<string> {
40
- const config = (
41
- await ctx.getConfig<Record<string, SmtpClientDefaultOptions | undefined>>("smtp")
42
- )[configName];
43
- if (config == null) {
44
- throw new Error(`SMTP config not found: ${configName}`);
45
- }
46
-
47
- return this.send({
48
- user: config.user,
49
- pass: config.pass,
50
- host: config.host,
51
- port: config.port,
52
- secure: config.secure,
53
- from: `"${config.senderName}" <${config.senderEmail ?? config.user}>`,
54
- ...options,
55
- });
56
- },
57
- }));
58
-
59
- export type SmtpClientServiceType = ServiceMethods<typeof SmtpClientService>;
@@ -1,66 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { defineService, auth, getServiceAuthPermissions } from "@simplysm/service-server";
3
-
4
- describe("defineService", () => {
5
- it("creates service definition with name and factory", () => {
6
- const svc = defineService("Health", (_ctx) => ({
7
- check: () => "ok",
8
- }));
9
-
10
- expect(svc.name).toBe("Health");
11
- expect(typeof svc.factory).toBe("function");
12
- });
13
-
14
- it("factory generates methods when called with context", () => {
15
- const svc = defineService("Echo", (_ctx) => ({
16
- echo: (msg: string) => `Echo: ${msg}`,
17
- }));
18
-
19
- const methods = svc.factory({} as any);
20
- expect(methods.echo("hello")).toBe("Echo: hello");
21
- });
22
- });
23
-
24
- describe("Authentication", () => {
25
- it("marks function with empty permissions (login required only)", () => {
26
- const fn = auth(() => "result");
27
- expect(getServiceAuthPermissions(fn)).toEqual([]);
28
- expect(fn()).toBe("result");
29
- });
30
-
31
- it("marks function with specific permissions", () => {
32
- const fn = auth(["admin"], (id: number) => id * 2);
33
- expect(getServiceAuthPermissions(fn)).toEqual(["admin"]);
34
- expect(fn(5)).toBe(10);
35
- });
36
-
37
- it("returns undefined for unmarked function", () => {
38
- const fn = () => "plain";
39
- expect(getServiceAuthPermissions(fn)).toBeUndefined();
40
- });
41
-
42
- it("works at service level (factory wrapping)", () => {
43
- const svc = defineService(
44
- "User",
45
- auth((_ctx) => ({
46
- getProfile: () => "profile",
47
- })),
48
- );
49
-
50
- expect(svc.authPermissions).toEqual([]);
51
- });
52
-
53
- it("method-level auth is readable from returned methods", () => {
54
- const svc = defineService(
55
- "Mixed",
56
- auth((_ctx) => ({
57
- normal: () => "normal",
58
- adminOnly: auth(["admin"], () => "admin"),
59
- })),
60
- );
61
-
62
- const methods = svc.factory({} as any);
63
- expect(getServiceAuthPermissions(methods.normal)).toBeUndefined();
64
- expect(getServiceAuthPermissions(methods.adminOnly)).toEqual(["admin"]);
65
- });
66
- });
@@ -1,83 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import type { QueryDef } from "@simplysm/orm-common";
3
-
4
- vi.mock("@simplysm/orm-node", () => ({
5
- createDbConn: vi.fn(),
6
- }));
7
-
8
- import { createDbConn } from "@simplysm/orm-node";
9
- import { OrmService } from "../src/services/orm-service";
10
-
11
- describe("OrmService.executeDefs", () => {
12
- let mockExecute: ReturnType<typeof vi.fn>;
13
- let methods: any;
14
- let connId: number;
15
-
16
- const twoDefs: QueryDef[] = [
17
- {
18
- type: "createTable",
19
- table: { database: "db", name: "A" },
20
- columns: [{ name: "id", dataType: { type: "bigint" } }],
21
- primaryKey: ["id"],
22
- },
23
- {
24
- type: "createTable",
25
- table: { database: "db", name: "B" },
26
- columns: [{ name: "id", dataType: { type: "bigint" } }],
27
- primaryKey: ["id"],
28
- },
29
- ];
30
-
31
- beforeEach(async () => {
32
- mockExecute = vi.fn((queries: string[]) => Promise.resolve(queries.map(() => [])));
33
-
34
- const mockConn = {
35
- config: { dialect: "postgresql" as const },
36
- isConnected: true,
37
- connect: vi.fn(),
38
- close: vi.fn(),
39
- execute: mockExecute,
40
- executeParametrized: vi.fn(),
41
- bulkInsert: vi.fn(),
42
- beginTransaction: vi.fn(),
43
- commitTransaction: vi.fn(),
44
- rollbackTransaction: vi.fn(),
45
- on: vi.fn(),
46
- };
47
-
48
- vi.mocked(createDbConn).mockResolvedValue(mockConn as any);
49
-
50
- const ctx = {
51
- socket: { on: vi.fn() },
52
- getConfig: vi.fn(() =>
53
- Promise.resolve({
54
- test: { dialect: "postgresql", host: "localhost", database: "db" },
55
- }),
56
- ),
57
- clientName: "test",
58
- };
59
-
60
- methods = OrmService.factory(ctx as any);
61
- connId = await methods.connect({ configName: "test" });
62
- mockExecute.mockClear();
63
- });
64
-
65
- it("executes queries individually when options is undefined", async () => {
66
- const result = await methods.executeDefs(connId, twoDefs);
67
-
68
- // Should pass 2 separate queries, not 1 combined string
69
- expect(mockExecute).toHaveBeenCalledTimes(1);
70
- expect(mockExecute.mock.calls[0][0]).toHaveLength(2);
71
-
72
- // Should return one result per def
73
- expect(result).toHaveLength(2);
74
- });
75
-
76
- it("combines queries when options is explicitly all-null", async () => {
77
- await methods.executeDefs(connId, twoDefs, [undefined, undefined]);
78
-
79
- // Should pass 1 combined query string
80
- expect(mockExecute).toHaveBeenCalledTimes(1);
81
- expect(mockExecute.mock.calls[0][0]).toHaveLength(1);
82
- });
83
- });
@@ -1,114 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { executeServiceMethod } from "../src/core/service-executor";
3
- import { defineService, auth } from "../src/core/define-service";
4
-
5
- // Minimal mock server
6
- function createMockServer(services: any[]) {
7
- return { options: { services, auth: { jwtSecret: "test" } } } as any;
8
- }
9
-
10
- describe("executeServiceMethod with ServiceDefinition", () => {
11
- it("executes a basic service method", async () => {
12
- const EchoService = defineService("Echo", (_ctx) => ({
13
- echo: (msg: string) => `Echo: ${msg}`,
14
- }));
15
-
16
- const server = createMockServer([EchoService]);
17
- const result = await executeServiceMethod(server, {
18
- serviceName: "Echo",
19
- methodName: "echo",
20
- params: ["hello"],
21
- });
22
-
23
- expect(result).toBe("Echo: hello");
24
- });
25
-
26
- it("throws error when service not found", async () => {
27
- const server = createMockServer([]);
28
-
29
- await expect(
30
- executeServiceMethod(server, { serviceName: "Unknown", methodName: "test", params: [] }),
31
- ).rejects.toThrow("Service [Unknown] not found.");
32
- });
33
-
34
- it("throws error when method not found", async () => {
35
- const svc = defineService("Test", (_ctx) => ({
36
- existing: () => "ok",
37
- }));
38
- const server = createMockServer([svc]);
39
-
40
- await expect(
41
- executeServiceMethod(server, { serviceName: "Test", methodName: "nonexistent", params: [] }),
42
- ).rejects.toThrow("Method [Test.nonexistent] not found.");
43
- });
44
-
45
- it("blocks unauthenticated access to auth-required service", async () => {
46
- const svc = defineService(
47
- "Protected",
48
- auth((_ctx) => ({
49
- secret: () => "secret",
50
- })),
51
- );
52
- const server = createMockServer([svc]);
53
-
54
- await expect(
55
- executeServiceMethod(server, { serviceName: "Protected", methodName: "secret", params: [] }),
56
- ).rejects.toThrow("Login is required.");
57
- });
58
-
59
- it("blocks unauthorized role access", async () => {
60
- const svc = defineService(
61
- "Admin",
62
- auth((_ctx) => ({
63
- manage: auth(["admin"], () => "managed"),
64
- view: () => "viewed",
65
- })),
66
- );
67
- const server = createMockServer([svc]);
68
-
69
- // Has auth but wrong role
70
- await expect(
71
- executeServiceMethod(server, {
72
- serviceName: "Admin",
73
- methodName: "manage",
74
- params: [],
75
- http: { clientName: "test", authTokenPayload: { roles: ["user"], data: {} } as any },
76
- }),
77
- ).rejects.toThrow("Insufficient permissions.");
78
- });
79
-
80
- it("allows access with correct role", async () => {
81
- const svc = defineService(
82
- "Admin",
83
- auth((_ctx) => ({
84
- manage: auth(["admin"], () => "managed"),
85
- })),
86
- );
87
- const server = createMockServer([svc]);
88
-
89
- const result = await executeServiceMethod(server, {
90
- serviceName: "Admin",
91
- methodName: "manage",
92
- params: [],
93
- http: { clientName: "test", authTokenPayload: { roles: ["admin"], data: {} } as any },
94
- });
95
-
96
- expect(result).toBe("managed");
97
- });
98
-
99
- it("provides context to factory", async () => {
100
- const svc = defineService("Ctx", (ctx) => ({
101
- getClientName: () => ctx.clientName,
102
- }));
103
- const server = createMockServer([svc]);
104
-
105
- const result = await executeServiceMethod(server, {
106
- serviceName: "Ctx",
107
- methodName: "getClientName",
108
- params: [],
109
- http: { clientName: "my-app", authTokenPayload: undefined },
110
- });
111
-
112
- expect(result).toBe("my-app");
113
- });
114
- });