@simplens/onboard 1.0.0 → 1.0.2

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 (82) hide show
  1. package/README.md +331 -214
  2. package/dist/__tests__/env-config.test.d.ts +2 -0
  3. package/dist/__tests__/env-config.test.d.ts.map +1 -0
  4. package/dist/__tests__/env-config.test.js +23 -0
  5. package/dist/__tests__/env-config.test.js.map +1 -0
  6. package/dist/__tests__/infra-prompts.test.d.ts +2 -0
  7. package/dist/__tests__/infra-prompts.test.d.ts.map +1 -0
  8. package/dist/__tests__/infra-prompts.test.js +43 -0
  9. package/dist/__tests__/infra-prompts.test.js.map +1 -0
  10. package/dist/__tests__/infra.test.d.ts +2 -0
  11. package/dist/__tests__/infra.test.d.ts.map +1 -0
  12. package/dist/__tests__/infra.test.js +14 -0
  13. package/dist/__tests__/infra.test.js.map +1 -0
  14. package/dist/__tests__/nginx.test.d.ts +2 -0
  15. package/dist/__tests__/nginx.test.d.ts.map +1 -0
  16. package/dist/__tests__/nginx.test.js +16 -0
  17. package/dist/__tests__/nginx.test.js.map +1 -0
  18. package/dist/env-config.d.ts +27 -12
  19. package/dist/env-config.d.ts.map +1 -1
  20. package/dist/env-config.js +258 -141
  21. package/dist/env-config.js.map +1 -1
  22. package/dist/index.js +341 -71
  23. package/dist/index.js.map +1 -1
  24. package/dist/infra.d.ts +17 -14
  25. package/dist/infra.d.ts.map +1 -1
  26. package/dist/infra.js +265 -176
  27. package/dist/infra.js.map +1 -1
  28. package/dist/plugins.d.ts +5 -10
  29. package/dist/plugins.d.ts.map +1 -1
  30. package/dist/plugins.js +75 -44
  31. package/dist/plugins.js.map +1 -1
  32. package/dist/services.d.ts +1 -23
  33. package/dist/services.d.ts.map +1 -1
  34. package/dist/services.js +47 -62
  35. package/dist/services.js.map +1 -1
  36. package/dist/templates.d.ts +3 -2
  37. package/dist/templates.d.ts.map +1 -1
  38. package/dist/templates.js +203 -198
  39. package/dist/templates.js.map +1 -1
  40. package/dist/types/domain.d.ts +2 -0
  41. package/dist/types/domain.d.ts.map +1 -1
  42. package/dist/ui.d.ts +45 -0
  43. package/dist/ui.d.ts.map +1 -0
  44. package/dist/ui.js +93 -0
  45. package/dist/ui.js.map +1 -0
  46. package/dist/utils/logger.d.ts +1 -0
  47. package/dist/utils/logger.d.ts.map +1 -1
  48. package/dist/utils/logger.js +32 -7
  49. package/dist/utils/logger.js.map +1 -1
  50. package/dist/utils.d.ts +8 -0
  51. package/dist/utils.d.ts.map +1 -1
  52. package/dist/utils.js +66 -2
  53. package/dist/utils.js.map +1 -1
  54. package/dist/validators.d.ts +1 -52
  55. package/dist/validators.d.ts.map +1 -1
  56. package/dist/validators.js +10 -57
  57. package/dist/validators.js.map +1 -1
  58. package/package.json +3 -5
  59. package/src/__tests__/env-config.test.ts +28 -0
  60. package/src/__tests__/errors.test.ts +187 -187
  61. package/src/__tests__/infra-prompts.test.ts +54 -0
  62. package/src/__tests__/infra.test.ts +15 -0
  63. package/src/__tests__/utils.test.ts +142 -142
  64. package/src/__tests__/validators.test.ts +195 -195
  65. package/src/config/constants.ts +86 -86
  66. package/src/config/index.ts +1 -1
  67. package/src/env-config.ts +455 -320
  68. package/src/index.ts +534 -203
  69. package/src/infra.ts +404 -300
  70. package/src/plugins.ts +221 -190
  71. package/src/services.ts +175 -190
  72. package/src/templates.ts +209 -203
  73. package/src/types/domain.ts +129 -127
  74. package/src/types/errors.ts +173 -173
  75. package/src/types/index.ts +2 -2
  76. package/src/ui.ts +91 -0
  77. package/src/utils/index.ts +1 -1
  78. package/src/utils/logger.ts +144 -118
  79. package/src/utils.ts +183 -105
  80. package/src/validators.ts +145 -192
  81. package/tsconfig.json +18 -18
  82. package/vitest.config.ts +22 -20
package/dist/templates.js CHANGED
@@ -1,202 +1,207 @@
1
1
  // Docker Compose templates as constants
2
- export const INFRA_COMPOSE_TEMPLATE = `# ============================================
3
- # INFRA_HOST: Set this in .env to your machine's IP/hostname when running
4
- # infrastructure on a separate system from application services.
5
- # Default: host.docker.internal (for same-system deployment)
6
- # ============================================
7
-
8
- services:
9
- # ============================================
10
- # Infrastructure Services
11
- # ============================================
12
- mongo:
13
- image: mongo:7.0
14
- container_name: mongo
15
- command: [ "--replSet", "rs0", "--bind_ip_all", "--port", "27017" ]
16
- ports:
17
- - 27017:27017
18
- extra_hosts:
19
- - "host.docker.internal:host-gateway"
20
- healthcheck:
21
- test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'{{INFRA_HOST}}:27017'}]}) }" | mongosh --port 27017 --quiet
22
- interval: 5s
23
- timeout: 30s
24
- start_period: 0s
25
- start_interval: 1s
26
- retries: 30
27
- volumes:
28
- - "mongo_data:/data/db"
29
- - "mongo_config:/data/configdb"
30
-
31
- kafka:
32
- image: apache/kafka-native
33
- container_name: kafka
34
- ports:
35
- - "9092:9092"
36
- environment:
37
- # Configure listeners for both docker and host communication
38
- KAFKA_LISTENERS: CONTROLLER://localhost:9091,HOST://0.0.0.0:9092,DOCKER://0.0.0.0:9093
39
- KAFKA_ADVERTISED_LISTENERS: HOST://{{INFRA_HOST}}:9092,DOCKER://kafka:9093
40
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,DOCKER:PLAINTEXT,HOST:PLAINTEXT
41
-
42
- # Settings required for KRaft mode
43
- KAFKA_NODE_ID: 1
44
- KAFKA_PROCESS_ROLES: broker,controller
45
- KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
46
- KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9091
47
-
48
- # Listener to use for broker-to-broker communication
49
- KAFKA_INTER_BROKER_LISTENER_NAME: DOCKER
50
-
51
- # Required for a single node cluster
52
- KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
53
-
54
- # Disable auto-topic creation - API server will create topics with correct partitions
55
- KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
56
- volumes:
57
- - "kafka_data:/var/lib/kafka/data"
58
-
59
- kafka-ui:
60
- image: kafbat/kafka-ui:main
61
- container_name: kafka-ui
62
- ports:
63
- - 8080:8080
64
- environment:
65
- DYNAMIC_CONFIG_ENABLED: "true"
66
- KAFKA_CLUSTERS_0_NAME: local
67
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9093
68
- depends_on:
69
- - kafka
70
-
71
- redis:
72
- image: redis:7-alpine
73
- container_name: redis
74
- ports:
75
- - "6379:6379"
76
- command: redis-server --appendonly yes
77
- volumes:
78
- - "redis_data:/data"
79
- healthcheck:
80
- test: [ "CMD", "redis-cli", "ping" ]
81
- interval: 5s
82
- timeout: 3s
83
- retries: 5
84
-
85
- # ============================================
86
- # Observability Services
87
- # ============================================
88
-
89
- loki:
90
- image: grafana/loki:2.9.0
91
- container_name: loki
92
- ports:
93
- - "3100:3100"
94
- command: -config.file=/etc/loki/local-config.yaml
95
- volumes:
96
- - "loki_data:/loki"
97
- healthcheck:
98
- test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3100/ready || exit 1" ]
99
- interval: 10s
100
- timeout: 5s
101
- retries: 5
102
-
103
- grafana:
104
- image: grafana/grafana:10.2.0
105
- container_name: grafana
106
- ports:
107
- - "3001:3000"
108
- environment:
109
- - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
110
- - GF_AUTH_ANONYMOUS_ENABLED=true
111
- - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
112
- - GF_SECURITY_ADMIN_PASSWORD=admin
113
- volumes:
114
- - "grafana_data:/var/lib/grafana"
115
- depends_on:
116
- loki:
117
- condition: service_healthy
118
-
119
- volumes:
120
- mongo_data:
121
- mongo_config:
122
- kafka_data:
123
- redis_data:
124
- loki_data:
125
- grafana_data:
2
+ export const INFRA_COMPOSE_TEMPLATE = `
3
+ services:
4
+ # ============================================
5
+ # Infrastructure Services
6
+ # ============================================
7
+ mongo:
8
+ image: mongo:7.0
9
+ container_name: mongo
10
+ command: [ "--replSet", "rs0", "--bind_ip_all", "--port", "27017" ]
11
+ ports:
12
+ - 27017:27017
13
+ healthcheck:
14
+ test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'mongo:27017'}]}) }" | mongosh --port 27017 --quiet
15
+ interval: 5s
16
+ timeout: 30s
17
+ start_period: 0s
18
+ start_interval: 1s
19
+ retries: 30
20
+ volumes:
21
+ - "mongo_data:/data/db"
22
+ - "mongo_config:/data/configdb"
23
+
24
+ kafka:
25
+ image: apache/kafka-native
26
+ container_name: kafka
27
+ ports:
28
+ - "9092:9092"
29
+ environment:
30
+ # Configure listeners for both docker and host communication
31
+ KAFKA_LISTENERS: CONTROLLER://localhost:9091,HOST://0.0.0.0:9092,DOCKER://0.0.0.0:9093
32
+ KAFKA_ADVERTISED_LISTENERS: HOST://kafka:9092,DOCKER://kafka:9093
33
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,DOCKER:PLAINTEXT,HOST:PLAINTEXT
34
+
35
+ # Settings required for KRaft mode
36
+ KAFKA_NODE_ID: 1
37
+ KAFKA_PROCESS_ROLES: broker,controller
38
+ KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
39
+ KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9091
40
+
41
+ # Listener to use for broker-to-broker communication
42
+ KAFKA_INTER_BROKER_LISTENER_NAME: DOCKER
43
+
44
+ # Required for a single node cluster
45
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
46
+
47
+ # Disable auto-topic creation - API server will create topics with correct partitions
48
+ KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false"
49
+ volumes:
50
+ - "kafka_data:/var/lib/kafka/data"
51
+
52
+ kafka-ui:
53
+ image: kafbat/kafka-ui:main
54
+ container_name: kafka-ui
55
+ ports:
56
+ - 8080:8080
57
+ environment:
58
+ DYNAMIC_CONFIG_ENABLED: "true"
59
+ KAFKA_CLUSTERS_0_NAME: local
60
+ KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9093
61
+ depends_on:
62
+ - kafka
63
+
64
+ redis:
65
+ image: redis:7-alpine
66
+ container_name: redis
67
+ ports:
68
+ - "6379:6379"
69
+ command: redis-server --appendonly yes
70
+ volumes:
71
+ - "redis_data:/data"
72
+ healthcheck:
73
+ test: [ "CMD", "redis-cli", "ping" ]
74
+ interval: 5s
75
+ timeout: 3s
76
+ retries: 5
77
+
78
+ # ============================================
79
+ # Observability Services
80
+ # ============================================
81
+
82
+ loki:
83
+ image: grafana/loki:2.9.0
84
+ container_name: loki
85
+ ports:
86
+ - "3100:3100"
87
+ command: -config.file=/etc/loki/local-config.yaml
88
+ volumes:
89
+ - "loki_data:/loki"
90
+ healthcheck:
91
+ test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3100/ready || exit 1" ]
92
+ interval: 10s
93
+ timeout: 5s
94
+ retries: 5
95
+
96
+ grafana:
97
+ image: grafana/grafana:10.2.0
98
+ container_name: grafana
99
+ ports:
100
+ - "3001:3000"
101
+ environment:
102
+ - GF_PATHS_PROVISIONING=/etc/grafana/provisioning
103
+ - GF_AUTH_ANONYMOUS_ENABLED=true
104
+ - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
105
+ - GF_SECURITY_ADMIN_PASSWORD=admin
106
+ volumes:
107
+ - "grafana_data:/var/lib/grafana"
108
+ depends_on:
109
+ loki:
110
+ condition: service_healthy
111
+
112
+ volumes:
113
+ mongo_data:
114
+ mongo_config:
115
+ kafka_data:
116
+ redis_data:
117
+ loki_data:
118
+ grafana_data:
126
119
  `;
127
- export const APP_COMPOSE_TEMPLATE = `services:
128
- api:
129
- image: ghcr.io/simplenotificationsystem/simplens-core:latest
130
- container_name: api
131
- ports:
132
- - 3000:3000
133
- env_file:
134
- - .env
135
- volumes:
136
- - plugin-data:/app/.plugins
137
- - ./simplens.config.yaml:/app/simplens.config.yaml:ro
138
- command: [ "node", "dist/api/server.js" ]
139
- restart: unless-stopped
140
- healthcheck:
141
- test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))" ]
142
- interval: 30s
143
- timeout: 10s
144
- retries: 3
145
- start_period: 10s
146
-
147
- worker:
148
- image: ghcr.io/simplenotificationsystem/simplens-core:latest
149
- env_file:
150
- - .env
151
- command: [ "node", "dist/workers/worker.js" ]
152
- restart: unless-stopped
153
-
154
- notification_processor:
155
- image: ghcr.io/simplenotificationsystem/simplens-core:latest
156
- env_file:
157
- - .env
158
- volumes:
159
- - plugin-data:/app/.plugins
160
- - ./simplens.config.yaml:/app/simplens.config.yaml:ro
161
- command: [ "node", "dist/processors/unified/unified.processor.js" ]
162
- depends_on:
163
- api:
164
- condition: service_healthy
165
- restart: unless-stopped
166
-
167
- delayed_processor:
168
- image: ghcr.io/simplenotificationsystem/simplens-core:latest
169
- env_file:
170
- - .env
171
- command: [ "node", "dist/processors/delayed/delayed.processor.js" ]
172
- restart: unless-stopped
173
-
174
- recovery:
175
- image: ghcr.io/simplenotificationsystem/simplens-core:latest
176
- env_file:
177
- - .env
178
- command: [ "node", "dist/workers/recovery/recovery.service.js" ]
179
- restart: unless-stopped
180
-
181
- dashboard:
182
- image: ghcr.io/simplenotificationsystem/simplens-dashboard:latest
183
- ports:
184
- - 3002:3002
185
- container_name: dashboard
186
- env_file:
187
- - .env
188
- environment:
189
- PORT: \${DASHBOARD_PORT:-3002}
190
- API_BASE_URL: http://api:\${PORT:-3000}
191
- WEBHOOK_HOST: dashboard
192
- WEBHOOK_PORT: \${DASHBOARD_PORT:-3002}
193
- restart: unless-stopped
194
-
195
- volumes:
196
- plugin-data:
197
-
198
- networks:
199
- default:
200
- name: simplens
120
+ export const APP_COMPOSE_TEMPLATE = `services:
121
+ api:
122
+ image: ghcr.io/simplenotificationsystem/simplens-core:\${CORE_VERSION:-latest}
123
+ container_name: api
124
+ ports:
125
+ - 3000:3000
126
+ env_file:
127
+ - .env
128
+ volumes:
129
+ - plugin-data:/app/.plugins
130
+ - ./simplens.config.yaml:/app/simplens.config.yaml:ro
131
+ command: [ "node", "dist/api/server.js" ]
132
+ restart: unless-stopped
133
+ healthcheck:
134
+ test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))" ]
135
+ interval: 30s
136
+ timeout: 10s
137
+ retries: 3
138
+ start_period: 10s
139
+
140
+ worker:
141
+ image: ghcr.io/simplenotificationsystem/simplens-core:\${CORE_VERSION:-latest}
142
+ env_file:
143
+ - .env
144
+ command: [ "node", "dist/workers/worker.js" ]
145
+ restart: unless-stopped
146
+
147
+ notification_processor:
148
+ image: ghcr.io/simplenotificationsystem/simplens-core:\${CORE_VERSION:-latest}
149
+ env_file:
150
+ - .env
151
+ volumes:
152
+ - plugin-data:/app/.plugins
153
+ - ./simplens.config.yaml:/app/simplens.config.yaml:ro
154
+ command: [ "node", "dist/processors/unified/unified.processor.js" ]
155
+ depends_on:
156
+ api:
157
+ condition: service_healthy
158
+ restart: unless-stopped
159
+
160
+ delayed_processor:
161
+ image: ghcr.io/simplenotificationsystem/simplens-core:\${CORE_VERSION:-latest}
162
+ env_file:
163
+ - .env
164
+ command: [ "node", "dist/processors/delayed/delayed.processor.js" ]
165
+ restart: unless-stopped
166
+
167
+ recovery:
168
+ image: ghcr.io/simplenotificationsystem/simplens-core:\${CORE_VERSION:-latest}
169
+ env_file:
170
+ - .env
171
+ command: [ "node", "dist/workers/recovery/recovery.service.js" ]
172
+ restart: unless-stopped
173
+
174
+ dashboard:
175
+ image: ghcr.io/simplenotificationsystem/simplens-dashboard:\${DASHBOARD_VERSION:-latest}
176
+ ports:
177
+ - 3002:3002
178
+ container_name: dashboard
179
+ env_file:
180
+ - .env
181
+ environment:
182
+ PORT: \${DASHBOARD_PORT:-3002}
183
+ API_BASE_URL: http://api:\${PORT:-3000}
184
+ WEBHOOK_HOST: dashboard
185
+ WEBHOOK_PORT: \${DASHBOARD_PORT:-3002}
186
+ NEXT_PUBLIC_BASE_PATH: \${BASE_PATH:-}
187
+ restart: unless-stopped
188
+
189
+ volumes:
190
+ plugin-data:
191
+
192
+ networks:
193
+ default:
194
+ name: simplens
201
195
  `;
196
+ export const APP_NGINX_SERVICE_TEMPLATE = ` nginx:
197
+ image: nginx:alpine
198
+ container_name: nginx
199
+ ports:
200
+ - "80:80"
201
+ volumes:
202
+ - "./nginx.conf:/etc/nginx/conf.d/default.conf:ro"
203
+ depends_on:
204
+ - api
205
+ - dashboard
206
+ restart: unless-stopped`;
202
207
  //# sourceMappingURL=templates.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4HrC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0EnC,CAAC"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqHrC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EnC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;4BAUd,CAAC"}
@@ -46,6 +46,8 @@ export interface SetupOptions {
46
46
  envMode: 'default' | 'interactive';
47
47
  /** Target directory for setup */
48
48
  targetDir: string;
49
+ /** Dashboard base path (empty string means root) */
50
+ basePath: string;
49
51
  }
50
52
  /**
51
53
  * Service definition for docker-compose generation
@@ -1 +1 @@
1
- {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/types/domain.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/types/domain.ts"],"names":[],"mappings":"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"}
package/dist/ui.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Centralized UI helpers — themed intro/outro and cancellation handling.
3
+ *
4
+ * NOTE: Individual @clack/prompts functions (text, confirm, select, etc.)
5
+ * should be imported directly from '@clack/prompts' in each consumer file.
6
+ * Re-exporting from here causes TypeScript resolution issues with .d.mts types.
7
+ */
8
+ /**
9
+ * Themed intro — blue bar
10
+ */
11
+ export declare function intro(title?: string): void;
12
+ /**
13
+ * Themed outro — blue text
14
+ */
15
+ export declare function outro(message?: string): void;
16
+ /**
17
+ * Wrapped log functions that respect silent mode
18
+ */
19
+ export declare const log: {
20
+ step: (message: string) => void;
21
+ info: (message: string) => void;
22
+ warning: (message: string) => void;
23
+ error: (message: string) => void;
24
+ success: (message: string) => void;
25
+ };
26
+ /**
27
+ * Wrapped note function that respects silent mode
28
+ */
29
+ export declare function note(message: string, title?: string): void;
30
+ /**
31
+ * Wrapped spinner that respects silent mode
32
+ * Returns a no-op spinner in silent mode
33
+ */
34
+ export declare function spinner(): import("@clack/prompts").SpinnerResult | {
35
+ start: () => void;
36
+ stop: () => void;
37
+ message: () => void;
38
+ error: () => void;
39
+ };
40
+ /**
41
+ * Handle user cancellation (Ctrl-C) for any prompt value.
42
+ * Exits the process with code 0 after printing a message.
43
+ */
44
+ export declare function handleCancel(value: unknown, message?: string): void;
45
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAG1C;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAG5C;AAED;;GAEG;AACH,eAAO,MAAM,GAAG;oBACI,MAAM;oBAIN,MAAM;uBAIH,MAAM;qBAIR,MAAM;uBAIJ,MAAM;CAI5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAG1D;AAED;;;GAGG;AACH,wBAAgB,OAAO;;;;;EAWtB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,SAAqB,GAAG,IAAI,CAO/E"}
package/dist/ui.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Centralized UI helpers — themed intro/outro and cancellation handling.
3
+ *
4
+ * NOTE: Individual @clack/prompts functions (text, confirm, select, etc.)
5
+ * should be imported directly from '@clack/prompts' in each consumer file.
6
+ * Re-exporting from here causes TypeScript resolution issues with .d.mts types.
7
+ */
8
+ import { intro as clackIntro, outro as clackOutro, cancel as clackCancel, isCancel, log as clackLog, note as clackNote, spinner as clackSpinner } from '@clack/prompts';
9
+ import chalk from 'chalk';
10
+ import { getLoggerConfig } from './utils/logger.js';
11
+ /**
12
+ * Themed intro — blue bar
13
+ */
14
+ export function intro(title) {
15
+ if (getLoggerConfig().silent)
16
+ return;
17
+ clackIntro(chalk.bgBlueBright.black(` ${title ?? ''} `));
18
+ }
19
+ /**
20
+ * Themed outro — blue text
21
+ */
22
+ export function outro(message) {
23
+ if (getLoggerConfig().silent)
24
+ return;
25
+ clackOutro(chalk.blueBright(message ?? 'Done'));
26
+ }
27
+ /**
28
+ * Wrapped log functions that respect silent mode
29
+ */
30
+ export const log = {
31
+ step: (message) => {
32
+ if (getLoggerConfig().silent)
33
+ return;
34
+ clackLog.step(message);
35
+ },
36
+ info: (message) => {
37
+ if (getLoggerConfig().silent)
38
+ return;
39
+ clackLog.info(message);
40
+ },
41
+ warning: (message) => {
42
+ if (getLoggerConfig().silent)
43
+ return;
44
+ clackLog.warning(message);
45
+ },
46
+ error: (message) => {
47
+ if (getLoggerConfig().silent)
48
+ return;
49
+ clackLog.error(message);
50
+ },
51
+ success: (message) => {
52
+ if (getLoggerConfig().silent)
53
+ return;
54
+ clackLog.success(message);
55
+ },
56
+ };
57
+ /**
58
+ * Wrapped note function that respects silent mode
59
+ */
60
+ export function note(message, title) {
61
+ if (getLoggerConfig().silent)
62
+ return;
63
+ clackNote(message, title);
64
+ }
65
+ /**
66
+ * Wrapped spinner that respects silent mode
67
+ * Returns a no-op spinner in silent mode
68
+ */
69
+ export function spinner() {
70
+ if (getLoggerConfig().silent) {
71
+ // Return a no-op spinner with all required methods
72
+ return {
73
+ start: () => { },
74
+ stop: () => { },
75
+ message: () => { },
76
+ error: () => { },
77
+ };
78
+ }
79
+ return clackSpinner();
80
+ }
81
+ /**
82
+ * Handle user cancellation (Ctrl-C) for any prompt value.
83
+ * Exits the process with code 0 after printing a message.
84
+ */
85
+ export function handleCancel(value, message = 'Setup cancelled.') {
86
+ if (isCancel(value)) {
87
+ if (!getLoggerConfig().silent) {
88
+ clackCancel(chalk.blueBright(message));
89
+ }
90
+ process.exit(0);
91
+ }
92
+ }
93
+ //# sourceMappingURL=ui.js.map
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,IAAI,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAI,QAAQ,EAAE,IAAI,IAAI,SAAS,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACxK,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,KAAc;IAChC,IAAI,eAAe,EAAE,CAAC,MAAM;QAAE,OAAO;IACrC,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,OAAgB;IAClC,IAAI,eAAe,EAAE,CAAC,MAAM;QAAE,OAAO;IACrC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG;IACf,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE;QACtB,IAAI,eAAe,EAAE,CAAC,MAAM;YAAE,OAAO;QACrC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE;QACtB,IAAI,eAAe,EAAE,CAAC,MAAM;YAAE,OAAO;QACrC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE;QACzB,IAAI,eAAe,EAAE,CAAC,MAAM;YAAE,OAAO;QACrC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,EAAE,CAAC,OAAe,EAAE,EAAE;QACvB,IAAI,eAAe,EAAE,CAAC,MAAM;YAAE,OAAO;QACrC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE;QACzB,IAAI,eAAe,EAAE,CAAC,MAAM;YAAE,OAAO;QACrC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,OAAe,EAAE,KAAc;IAChD,IAAI,eAAe,EAAE,CAAC,MAAM;QAAE,OAAO;IACrC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO;IACnB,IAAI,eAAe,EAAE,CAAC,MAAM,EAAE,CAAC;QAC3B,mDAAmD;QACnD,OAAO;YACH,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;YACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;YACd,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;SAClB,CAAC;IACN,CAAC;IACD,OAAO,YAAY,EAAE,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,OAAO,GAAG,kBAAkB;IACrE,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAClB,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,CAAC;YAC5B,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC"}
@@ -8,6 +8,7 @@ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
8
  interface LoggerConfig {
9
9
  verbose: boolean;
10
10
  debug: boolean;
11
+ silent: boolean;
11
12
  logFile?: string;
12
13
  }
13
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D;;GAEG;AACH,UAAU,YAAY;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAE9D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAAC,YAAY,CAAC,CAExD;AAiBD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAK9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKhD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG7C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAEhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D;;GAEG;AACH,UAAU,YAAY;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAkCD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAE9D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAAC,YAAY,CAAC,CAExD;AAiBD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAK9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKhD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG7C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAEhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E"}