nsa-sheets-db-builder 4.0.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/bin/sheets-deployer.mjs +169 -0
  4. package/libs/alasql.js +15577 -0
  5. package/libs/common/gas_response_helper.ts +147 -0
  6. package/libs/common/gaserror.ts +101 -0
  7. package/libs/common/gaslogger.ts +172 -0
  8. package/libs/db_ddl.ts +316 -0
  9. package/libs/libraries.json +56 -0
  10. package/libs/spreadsheets_db.ts +4406 -0
  11. package/libs/triggers.ts +113 -0
  12. package/package.json +73 -0
  13. package/scripts/build.mjs +513 -0
  14. package/scripts/clean.mjs +31 -0
  15. package/scripts/create.mjs +94 -0
  16. package/scripts/ddl-handler.mjs +232 -0
  17. package/scripts/describe.mjs +38 -0
  18. package/scripts/drop.mjs +39 -0
  19. package/scripts/init.mjs +465 -0
  20. package/scripts/lib/utils.mjs +1019 -0
  21. package/scripts/login.mjs +102 -0
  22. package/scripts/provision.mjs +35 -0
  23. package/scripts/refresh-cache.mjs +34 -0
  24. package/scripts/set-key.mjs +48 -0
  25. package/scripts/setup-trigger.mjs +95 -0
  26. package/scripts/setup.mjs +677 -0
  27. package/scripts/show.mjs +37 -0
  28. package/scripts/sync.mjs +35 -0
  29. package/scripts/whoami.mjs +36 -0
  30. package/src/api/ddl-handler-entry.ts +136 -0
  31. package/src/api/ddl.ts +321 -0
  32. package/src/templates/.clasp.json.ejs +1 -0
  33. package/src/templates/appsscript.json.ejs +16 -0
  34. package/src/templates/config.ts.ejs +14 -0
  35. package/src/templates/ddl-handler-config.ts.ejs +3 -0
  36. package/src/templates/ddl-handler-main.ts.ejs +56 -0
  37. package/src/templates/main.ts.ejs +288 -0
  38. package/src/templates/rbac.ts.ejs +148 -0
  39. package/src/templates/views.ts.ejs +92 -0
  40. package/templates/blank.json +33 -0
  41. package/templates/blog-cms.json +507 -0
  42. package/templates/crm.json +360 -0
  43. package/templates/e-commerce.json +424 -0
  44. package/templates/inventory.json +307 -0
@@ -0,0 +1,147 @@
1
+ class GASStatusV2 {
2
+ type: 'OK' | 'WARNING' | 'ERROR';
3
+ message?: string;
4
+ timestamp: string;
5
+ traceId?: string;
6
+ error?: {
7
+ name: string;
8
+ source: string;
9
+ message: string;
10
+ context?: Record<string, any>;
11
+ originalError?: string;
12
+ };
13
+
14
+ constructor(type: 'OK' | 'WARNING' | 'ERROR', message?: string, traceId?: string, error?: any) {
15
+ this.type = type;
16
+ this.timestamp = formatDateTime();
17
+ this.traceId = traceId;
18
+
19
+ // Set message for OK and WARNING types
20
+ if ((type === 'OK' || type === 'WARNING') && message) {
21
+ this.message = message;
22
+ }
23
+
24
+ // Embed error details in status for ERROR type
25
+ if (type === 'ERROR' && error) {
26
+ this.error = error;
27
+ }
28
+ }
29
+
30
+ static OK(message?: string, traceId?: string): GASStatusV2 {
31
+ return new GASStatusV2('OK', message, traceId);
32
+ }
33
+
34
+ static Warning(message?: string, traceId?: string): GASStatusV2 {
35
+ return new GASStatusV2('WARNING', message, traceId);
36
+ }
37
+
38
+ static Error(traceId?: string, error?: any): GASStatusV2 {
39
+ return new GASStatusV2('ERROR', undefined, traceId, error);
40
+ }
41
+ }
42
+
43
+ class GASResponseV2 {
44
+ status: GASStatusV2;
45
+ result?: { [key: string]: any };
46
+
47
+ constructor(status: GASStatusV2, result?: { [key: string]: any }) {
48
+ this.status = status;
49
+
50
+ // Only include result for success/warning if provided
51
+ if ((status.type === 'OK' || status.type === 'WARNING') && result !== undefined) {
52
+ this.result = result;
53
+ }
54
+ }
55
+
56
+ static success(message?: string, result?: { [key: string]: any }, traceId?: string): GASResponseV2 {
57
+ return new GASResponseV2(GASStatusV2.OK(message, traceId), result);
58
+ }
59
+
60
+ static warning(message?: string, result?: { [key: string]: any }, traceId?: string): GASResponseV2 {
61
+ return new GASResponseV2(GASStatusV2.Warning(message, traceId), result);
62
+ }
63
+
64
+ static error(error: any): GASResponseV2 {
65
+ let errorObj: any;
66
+ let traceId: string | undefined;
67
+
68
+ if (error && error.name === 'GASErrorV2') {
69
+ traceId = error.traceId;
70
+ errorObj = {
71
+ name: error.name,
72
+ source: error.source,
73
+ message: error.message
74
+ };
75
+
76
+ // Only include context if not empty
77
+ if (error.context && Object.keys(error.context).length > 0) {
78
+ errorObj.context = error.context;
79
+ }
80
+
81
+ // Only include originalError if it exists and is not empty
82
+ if (error.originalError && error.originalError.trim() !== '') {
83
+ errorObj.originalError = error.originalError;
84
+ }
85
+ } else {
86
+ errorObj = {
87
+ name: 'Error',
88
+ source: 'Unknown',
89
+ message: error?.message || error?.toString() || 'Unknown error'
90
+ };
91
+ }
92
+
93
+ return new GASResponseV2(GASStatusV2.Error(traceId, errorObj));
94
+ }
95
+
96
+ toString(): string {
97
+ return JSON.stringify(this, null, 2);
98
+ }
99
+
100
+ getTraceId(): string | undefined {
101
+ return this.status.traceId;
102
+ }
103
+ }
104
+
105
+ class GASResponseBuilderV2 {
106
+ private traceId?: string;
107
+ private startTime?: number;
108
+ private operationId?: string;
109
+
110
+ constructor(traceId?: string) {
111
+ this.traceId = traceId;
112
+ this.startTime = new Date().getTime();
113
+ this.operationId = Utilities.getUuid();
114
+ }
115
+
116
+ setTraceId(traceId: string): this {
117
+ this.traceId = traceId;
118
+ return this;
119
+ }
120
+
121
+ setOperationId(operationId: string): this {
122
+ this.operationId = operationId;
123
+ return this;
124
+ }
125
+
126
+ success(message?: string, result?: { [key: string]: any }): GASResponseV2 {
127
+ return GASResponseV2.success(message, result, this.traceId);
128
+ }
129
+
130
+ warning(message?: string, result?: { [key: string]: any }): GASResponseV2 {
131
+ return GASResponseV2.warning(message, result, this.traceId);
132
+ }
133
+
134
+ error(error: any): GASResponseV2 {
135
+ // Ensure error has traceId set if it's a GASErrorV2
136
+ if (error && error.name === 'GASErrorV2' && !error.traceId && this.traceId) {
137
+ error.setTraceId(this.traceId);
138
+ }
139
+ return GASResponseV2.error(error);
140
+ }
141
+
142
+ criticalError(message: string): never {
143
+ throw new Error(message);
144
+ }
145
+ }
146
+
147
+ export { GASStatusV2, GASResponseV2, GASResponseBuilderV2 };
@@ -0,0 +1,101 @@
1
+ class GASErrorV2 extends Error {
2
+ public name: string;
3
+ public source: string;
4
+ public context: Record<string, any>;
5
+ public timestamp: string;
6
+ public traceId: string;
7
+ public originalError: string;
8
+
9
+ constructor(
10
+ message: string,
11
+ source: string,
12
+ context: Record<string, any> = {},
13
+ traceId?: string
14
+ ) {
15
+ super(message);
16
+ this.name = 'GASErrorV2';
17
+ this.message = message;
18
+ this.source = source;
19
+ this.context = context;
20
+ this.timestamp = formatDateTime();
21
+ this.traceId = traceId || '';
22
+ this.originalError = '';
23
+ }
24
+
25
+ setTraceId(traceId: string): this {
26
+ this.traceId = traceId;
27
+ return this;
28
+ }
29
+
30
+ getTraceId(): string {
31
+ return this.traceId;
32
+ }
33
+
34
+ static isGASErrorV2(error: any): boolean {
35
+ return error && error.name === 'GASErrorV2';
36
+ }
37
+
38
+ static handleError(
39
+ error: any,
40
+ message: string,
41
+ source: string,
42
+ context: Record<string, any> = {},
43
+ traceId?: string
44
+ ): never {
45
+ throw GASErrorV2.wrapError(error, message, source, context, traceId);
46
+ }
47
+
48
+ static wrapError(
49
+ error: any,
50
+ message: string,
51
+ source: string,
52
+ context: Record<string, any> = {},
53
+ traceId?: string
54
+ ): GASErrorV2 {
55
+ const newError = new GASErrorV2(message, source, context, traceId);
56
+
57
+ if (GASErrorV2.isGASErrorV2(error)) {
58
+ newError.originalError = error.message;
59
+ if (!traceId && error.traceId) {
60
+ newError.traceId = error.traceId;
61
+ }
62
+ } else if (error) {
63
+ newError.originalError = error.toString();
64
+ }
65
+
66
+ return newError;
67
+ }
68
+
69
+ static fromErrorGASResponseV2(response: any): GASErrorV2 {
70
+ const parsedError = response.error;
71
+ if (parsedError && parsedError.name === 'GASErrorV2' && parsedError.message && parsedError.source) {
72
+ const err = new GASErrorV2(
73
+ parsedError.message,
74
+ parsedError.source,
75
+ parsedError.context || {},
76
+ parsedError.traceId
77
+ );
78
+ err.originalError = parsedError.originalError || '';
79
+ return err;
80
+ }
81
+ throw new GASErrorV2(
82
+ "Invalid GASErrorV2 response",
83
+ "GASErrorV2.fromErrorGASResponseV2",
84
+ { response }
85
+ );
86
+ }
87
+
88
+ toJson(): string {
89
+ return JSON.stringify({
90
+ name: this.name,
91
+ source: this.source,
92
+ message: this.message,
93
+ context: this.context,
94
+ timestamp: this.timestamp,
95
+ traceId: this.traceId,
96
+ originalError: this.originalError
97
+ }, null, 2);
98
+ }
99
+ }
100
+
101
+ export default GASErrorV2;
@@ -0,0 +1,172 @@
1
+ class GASLoggerV2 {
2
+ private webhookUrl: string | null;
3
+ private verbosityLevel: number;
4
+ private loggerId: string;
5
+ private traceId: string;
6
+
7
+ constructor(loggerId: string, webhookUrl: string | null = null, verbosityLevel: number = 1, traceId: string = '') {
8
+ this.loggerId = loggerId;
9
+ this.webhookUrl = webhookUrl;
10
+ this.verbosityLevel = verbosityLevel;
11
+ this.traceId = traceId;
12
+ }
13
+
14
+ setVerbosityLevel(level: number): void {
15
+ this.verbosityLevel = level;
16
+ }
17
+
18
+ setTraceId(traceId: string): void {
19
+ this.traceId = traceId;
20
+ }
21
+
22
+ getTraceId(): string {
23
+ return this.traceId;
24
+ }
25
+
26
+ error(error: GASErrorV2): void {
27
+ const originalErrorArray = error.originalError ? [error.originalError] : [];
28
+
29
+ this.log('ERROR', error.source, error.message,
30
+ error.context,
31
+ originalErrorArray,
32
+ error.traceId
33
+ );
34
+ }
35
+
36
+ warn(source: string, message: string, context: any = {}, errors: any[] = [], traceId?: string): void {
37
+ this.log('WARNING', source, message, context, errors, traceId);
38
+ }
39
+
40
+ info(source: string, message: string, context: any, traceId?: string): void {
41
+ this.log('INFO', source, message, context, [], traceId);
42
+ }
43
+
44
+ debug(source: string, message: string, context: any = {}, errors: any[] = [], traceId?: string): void {
45
+ this.log('DEBUG', source, message, context, errors, traceId);
46
+ }
47
+
48
+ private log(type: 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR', source: string, message: string, context: any = {}, errors: any[] = [], traceId?: string): void {
49
+
50
+ if (
51
+ (this.verbosityLevel === 1 && type === 'ERROR') ||
52
+ (this.verbosityLevel === 2 && (type === 'ERROR' || type === 'WARNING')) ||
53
+ (this.verbosityLevel === 3 && (type === 'ERROR' || type === 'WARNING' || type === 'INFO')) ||
54
+ (this.verbosityLevel === 4)
55
+ ) {
56
+ const logId = this.generateLogId();
57
+ const timestamp = formatDateTime();
58
+ const fullSource = `${source}`;
59
+ const effectiveTraceId = traceId || this.traceId;
60
+
61
+ const logEntry: any = {
62
+ type: type,
63
+ source: fullSource,
64
+ message: message,
65
+ timestamp: timestamp,
66
+ logId: logId,
67
+ traceId: effectiveTraceId,
68
+ pipeline: this.loggerId,
69
+
70
+ };
71
+ if (context) {
72
+ logEntry.context = context;
73
+ }
74
+ if (errors) {
75
+ logEntry.errors = errors;
76
+ }
77
+
78
+ this.outputToConsole(type, logEntry);
79
+
80
+ if (type === 'ERROR' && this.webhookUrl) {
81
+ this.sendToChat(logEntry);
82
+ }
83
+ }
84
+ }
85
+
86
+ private outputToConsole(type: string, logEntry: any): void {
87
+ switch (type) {
88
+ case 'ERROR':
89
+ console.error(JSON.stringify(logEntry, null, 2));
90
+ break;
91
+ case 'WARNING':
92
+ console.warn(logEntry);
93
+ break;
94
+ case 'INFO':
95
+ console.log(logEntry);
96
+ break;
97
+ case 'DEBUG':
98
+ console.log(logEntry);
99
+ break;
100
+ }
101
+ }
102
+
103
+ private sendToChat(logEntry: { [key: string]: any }): void {
104
+ if (!this.webhookUrl) return;
105
+
106
+ const errorIcon = logEntry.type === 'ERROR' ? '❗ ' : '';
107
+
108
+ const jsonCardData: CardData = {
109
+ type: 'json',
110
+ header: `${errorIcon}${logEntry.source}`,
111
+ widgetSpecs: [
112
+ {
113
+ header: 'Error',
114
+ collapsible: false,
115
+ data: {
116
+ 'Pipeline': logEntry.pipeline,
117
+ 'Message': logEntry.message,
118
+ 'Timestamp': logEntry.timestamp,
119
+ 'TraceId': logEntry.traceId,
120
+ },
121
+ },
122
+ {
123
+ header: 'Context',
124
+ collapsible: true,
125
+ data: logEntry.context || {},
126
+ },
127
+ {
128
+ header: 'Error Stack',
129
+ collapsible: true,
130
+ data: logEntry.errors || [],
131
+ },
132
+ {
133
+ header: 'Logs',
134
+ collapsible: false,
135
+ data: {
136
+ 'LogId': logEntry.logId,
137
+ 'TraceId': logEntry.traceId,
138
+ },
139
+ },
140
+ ],
141
+ };
142
+
143
+ const card = CardFactory.createCardSpec(jsonCardData);
144
+
145
+ const payload = {
146
+ cardsV2: [
147
+ {
148
+ cardId: 'errorCard',
149
+ card: card,
150
+ },
151
+ ],
152
+ };
153
+
154
+ const chatService = new GoogleChatService(this.webhookUrl);
155
+ chatService.sendMessage(JSON.stringify(payload));
156
+ }
157
+
158
+ private generateLogId(): string {
159
+ return Utilities.getUuid();
160
+ }
161
+
162
+ createChildLogger(traceId?: string): GASLoggerV2 {
163
+ return new GASLoggerV2(
164
+ this.loggerId,
165
+ this.webhookUrl,
166
+ this.verbosityLevel,
167
+ traceId || this.traceId
168
+ );
169
+ }
170
+ }
171
+
172
+ export default GASLoggerV2;