@gen3/toolsff 0.12.2 → 0.12.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.
@@ -113,42 +113,6 @@ const main = ()=>{
113
113
  short: 't',
114
114
  default: './colors.json'
115
115
  },
116
- primary: {
117
- type: 'string',
118
- short: 'p',
119
- default: '#532565'
120
- },
121
- secondary: {
122
- type: 'string',
123
- short: 's',
124
- default: '#982568'
125
- },
126
- accent: {
127
- type: 'string',
128
- short: 'a',
129
- default: '#E07C3E'
130
- },
131
- accentWarm: {
132
- type: 'string',
133
- default: '#E07C3E'
134
- },
135
- accentCool: {
136
- type: 'string',
137
- default: '#1552e0'
138
- },
139
- base: {
140
- type: 'string',
141
- short: 'b',
142
- default: '#858585'
143
- },
144
- table: {
145
- type: 'string',
146
- default: '#858585'
147
- },
148
- navigation: {
149
- type: 'string',
150
- default: '#eaeaea'
151
- },
152
116
  out: {
153
117
  type: 'string',
154
118
  short: 'o',
File without changes
File without changes
File without changes
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import nextEnv from '@next/env';
5
+ import { parseArgs } from 'node:util';
6
+
7
+ const { loadEnvConfig } = nextEnv;
8
+ // Load .env files before reading process.env.*
9
+ loadEnvConfig(process.cwd());
10
+ const GEN3_COMMONS_NAME = process.env.NEXT_PUBLIC_GEN3_COMMONS_NAME || 'gen3';
11
+ const PAGE_EXT_RE = /\.(tsx|ts|jsx|js)$/;
12
+ const DEFAULT_EXCLUDED_ROUTES = [
13
+ '/403',
14
+ '/404',
15
+ '/Login',
16
+ '/*',
17
+ '/api',
18
+ '/no-workspace-access'
19
+ ];
20
+ function toPosixPath(p) {
21
+ return p.split(path.sep).join('/');
22
+ }
23
+ function loadAuthConfig() {
24
+ const authConfigFile = path.join(process.cwd(), `config/${GEN3_COMMONS_NAME}`, 'authz.json');
25
+ const defaultAuthzConfigFile = path.join(process.cwd(), `config`, 'authz_default.json');
26
+ if (fs.existsSync(authConfigFile)) {
27
+ console.log(`Loading authz config from ${authConfigFile}`);
28
+ return JSON.parse(fs.readFileSync(authConfigFile).toString('utf8'));
29
+ }
30
+ if (fs.existsSync(defaultAuthzConfigFile)) {
31
+ console.log(`Loading authz config from ${defaultAuthzConfigFile}`);
32
+ return JSON.parse(fs.readFileSync(defaultAuthzConfigFile).toString('utf8'));
33
+ }
34
+ console.log(`No authz config found. Using default authz rules.`);
35
+ return {
36
+ '/Profile': {
37
+ loginRequired: true
38
+ },
39
+ '*': {
40
+ loginRequired: false
41
+ }
42
+ };
43
+ }
44
+ function normalizeRoute(route) {
45
+ let r = route.startsWith('/') ? route : `/${route}`;
46
+ r = r.replace(/\/{2,}/g, '/');
47
+ if (r.length > 1) r = r.replace(/\/$/, '');
48
+ return r;
49
+ }
50
+ // ✅ Convert Next.js pages-style dynamic segments to middleware matcher syntax
51
+ function routeToMiddlewareMatcher(route) {
52
+ // route examples:
53
+ // "/Explorer/[configId]" -> "/Explorer/:configId"
54
+ // "/notebook/[...notebook]" -> "/notebook/:notebook*"
55
+ // "/foo/[[...slug]]" -> "/foo/:slug*"
56
+ return route.replace(/\[\[\.\.\.(\w+)\]\]/g, ':$1*') // optional catch-all
57
+ .replace(/\[\.\.\.(\w+)\]/g, ':$1*') // catch-all
58
+ .replace(/\[(\w+)\]/g, ':$1'); // single segment
59
+ }
60
+ function filePathToRoute(relativeFilePosix) {
61
+ if (!PAGE_EXT_RE.test(relativeFilePosix)) return null;
62
+ if (relativeFilePosix === 'api' || relativeFilePosix.startsWith('api/')) return null;
63
+ const baseName = path.posix.basename(relativeFilePosix);
64
+ if (baseName.startsWith('_')) return null;
65
+ let noExt = relativeFilePosix.replace(PAGE_EXT_RE, '');
66
+ noExt = noExt.replace(/\/index$/i, '');
67
+ if (noExt === 'index') noExt = '';
68
+ return normalizeRoute(noExt);
69
+ }
70
+ function scanPagesRoutes(pagesDirAbs, pagesRootAbs) {
71
+ const routes = [];
72
+ if (!fs.existsSync(pagesDirAbs)) return routes;
73
+ const entries = fs.readdirSync(pagesDirAbs, {
74
+ withFileTypes: true
75
+ });
76
+ for (const entry of entries){
77
+ const fullPath = path.join(pagesDirAbs, entry.name);
78
+ if (entry.isDirectory()) {
79
+ if (entry.name === '.next') continue;
80
+ routes.push(...scanPagesRoutes(fullPath, pagesRootAbs));
81
+ continue;
82
+ }
83
+ if (!entry.isFile()) continue;
84
+ const relFromPages = path.relative(pagesRootAbs, fullPath);
85
+ const relPosix = toPosixPath(relFromPages);
86
+ const route = filePathToRoute(relPosix);
87
+ if (route) routes.push(route);
88
+ }
89
+ return routes;
90
+ }
91
+ function getPagesRoutes(opts = {}) {
92
+ const pagesDir = path.resolve(opts.pagesDir || './src/pages');
93
+ const excluded = new Set((opts.excludeRoutes || DEFAULT_EXCLUDED_ROUTES).map(normalizeRoute));
94
+ const routes = scanPagesRoutes(pagesDir, pagesDir).map(normalizeRoute).filter((r)=>{
95
+ if (excluded.has(r)) return false;
96
+ if (r === '/') return false;
97
+ return true;
98
+ });
99
+ return Array.from(new Set(routes)).sort((a, b)=>a.localeCompare(b));
100
+ }
101
+ function getMatcherFromAuthConfig(authConfig) {
102
+ const paths = Object.keys(authConfig?.routes ?? {});
103
+ const normalizedRoutes = paths.map((p)=>normalizeRoute(p));
104
+ const excluded = new Set(DEFAULT_EXCLUDED_ROUTES.map(normalizeRoute));
105
+ return normalizedRoutes.filter((r)=>!excluded.has(r));
106
+ }
107
+ // ✅ Generates the actual Next middleware entry file with literal matcher
108
+ function writeGeneratedMiddlewareEntryTs(matcher, opts = {}) {
109
+ const outputFile = path.resolve(opts.outputFile || './src/middleware.ts');
110
+ const body = `// @generated by tools/scanSitePaths.ts\n` + `// Do not edit manually. Edit src/middleware-impl.ts instead.\n` + `export { middleware } from './middleware-impl';\n\n` + `export const config = {\n` + ` matcher: ${JSON.stringify(matcher, null, 2)},\n` + `};\n`;
111
+ console.log(`Writing generated middleware entry to ${outputFile}`);
112
+ fs.mkdirSync(path.dirname(outputFile), {
113
+ recursive: true
114
+ });
115
+ fs.writeFileSync(outputFile, body);
116
+ }
117
+ /**
118
+ * Configures and generates middleware route matchers based on authentication and routing requirements.
119
+ *
120
+ * The function determines the set of middleware routes based on the authentication configuration and
121
+ * whether all pages require login. It generates appropriate route matchers for middleware processing
122
+ * and writes the resulting matchers to a TypeScript file for use in the application.
123
+ *
124
+ * @param {boolean} [useScanAllPaths=false] - A flag to determine whether to include all application routes
125
+ * in the middleware path matcher. If true, all application routes
126
+ * are scanned and included; otherwise, specific routes from the
127
+ * authentication configuration are considered.
128
+ */ const createMiddlewareRoutes = (useScanAllPaths = false)=>{
129
+ const authConfig = loadAuthConfig();
130
+ const allPagesRequireLogin = authConfig?.routes?.['*']?.loginRequired;
131
+ let routes;
132
+ if (allPagesRequireLogin) {
133
+ console.log('All pages require login. Adding all routes to middleware path matcher.');
134
+ routes = useScanAllPaths ? getPagesRoutes() : []; // setting to [] will cause the matcher to be set below
135
+ } else {
136
+ console.log('Some pages require login. Adding authz routes to middleware path matcher.');
137
+ routes = getMatcherFromAuthConfig(authConfig);
138
+ }
139
+ // ✅ Convert to valid middleware matcher patterns
140
+ let matcher = (routes || []).map(routeToMiddlewareMatcher);
141
+ if (matcher.length === 0) {
142
+ matcher = [
143
+ '/((?!_next/static|_next/image|_next/data|Login|api|403|404|no-workspace-access|favicon.ico|.*\\.ico$|.*\\.png$|.*\\.jpg$|.*\\.svg$|.*\\.json$).*)'
144
+ ];
145
+ }
146
+ console.log(`middleware matcher entries:\n${matcher.join('\n')}`);
147
+ writeGeneratedMiddlewareEntryTs(matcher);
148
+ };
149
+ function main() {
150
+ try {
151
+ const { values: { scanAllPages } } = parseArgs({
152
+ options: {
153
+ scanAllPages: {
154
+ type: 'boolean',
155
+ default: false
156
+ }
157
+ }
158
+ });
159
+ createMiddlewareRoutes(scanAllPages);
160
+ } catch (e) {
161
+ if (e instanceof Error) {
162
+ if (e.name === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
163
+ console.error(`Error: ${e.message}`);
164
+ // Optionally print usage or help here
165
+ } else if (e.name === 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE') {
166
+ console.error(`Error: Missing value for option.`);
167
+ } else {
168
+ console.error('An unexpected error occurred:', e.message);
169
+ }
170
+ } else console.error('An unknown error occurred');
171
+ process.exit(1);
172
+ }
173
+ }
174
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gen3/toolsff",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "tools for processing Gen3 commons content: color theme, icons, sharedFilters",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -22,7 +22,7 @@
22
22
  "getSharedMapping": "./dist/getSharedMapping.esm.js",
23
23
  "getDRSToHostname": "./dist/getDRSToHostname.esm.js",
24
24
  "getSharedFilters": "./dist/getSharedFilters.esm.js",
25
- "gdcGqlToGuppyGql": "./dist/gdcGqlToGuppyGql.esm.js"
25
+ "scanSitePaths": "./dist/scanSitePaths.esm.js"
26
26
  },
27
27
  "author": "Craig Barnes",
28
28
  "license": "Apache-2.0",
@@ -47,6 +47,5 @@
47
47
  "undici": "^6.21.1",
48
48
  "axios": "^1.8.2"
49
49
  }
50
- },
51
- "gitHead": "d6a8d9fa8fb76a6d9c8eb52bb71704775b1ebc57"
50
+ }
52
51
  }
@@ -1,148 +0,0 @@
1
- const getArgs = (defaults)=>{
2
- const args = {
3
- ...defaults
4
- };
5
- process.argv.slice(2, process.argv.length).forEach((arg)=>{
6
- // long arg
7
- if (arg.slice(0, 2) === '--') {
8
- const longArg = arg.split('=');
9
- const longArgFlag = longArg[0].slice(2, longArg[0].length);
10
- const longArgValue = longArg.length > 1 ? longArg[1] : true;
11
- args[longArgFlag] = longArgValue;
12
- } else if (arg[0] === '-') {
13
- const flags = arg.slice(1, arg.length).split('');
14
- flags.forEach((flag)=>{
15
- args[flag] = true;
16
- });
17
- }
18
- });
19
- return args;
20
- };
21
-
22
- const assertNever = (x)=>{
23
- throw Error(`Exhaustive comparison did not handle: ${x}`);
24
- };
25
- const handleGqlOperation = (handler, op)=>{
26
- switch(op.op){
27
- case '=':
28
- return handler.handleEquals(op);
29
- case '!=':
30
- return handler.handleNotEquals(op);
31
- case '<':
32
- return handler.handleLessThan(op);
33
- case '<=':
34
- return handler.handleLessThanOrEquals(op);
35
- case '>':
36
- return handler.handleGreaterThan(op);
37
- case '>=':
38
- return handler.handleGreaterThanOrEquals(op);
39
- // case "is":
40
- // return handler.handleMissing(op);
41
- // case "not":
42
- // return handler.handleExists(op);
43
- case 'in':
44
- return handler.handleIncludes(op);
45
- case 'exclude':
46
- return handler.handleExcludes(op);
47
- case 'excludeifany':
48
- return handler.handleExcludeIfAny(op);
49
- case 'and':
50
- return handler.handleIntersection(op);
51
- case 'or':
52
- return handler.handleUnion(op);
53
- default:
54
- return assertNever(op);
55
- }
56
- };
57
-
58
- // a handler to convert from gql to guppy gql
59
- class FromGDCToGen3 {
60
- constructor(){
61
- this.handleEquals = (op)=>({
62
- "=": {
63
- [op.content.field]: op.content.value
64
- }
65
- });
66
- this.handleNotEquals = (op)=>({
67
- "!=": {
68
- [op.content.field]: op.content.value
69
- }
70
- });
71
- this.handleLessThan = (op)=>({
72
- "<": {
73
- [op.content.field]: op.content.value
74
- }
75
- });
76
- this.handleLessThanOrEquals = (op)=>({
77
- "<=": {
78
- [op.content.field]: op.content.value
79
- }
80
- });
81
- this.handleGreaterThan = (op)=>({
82
- ">": {
83
- [op.content.field]: op.content.value
84
- }
85
- });
86
- this.handleGreaterThanOrEquals = (op)=>({
87
- ">=": {
88
- [op.content.field]: op.content.value
89
- }
90
- });
91
- // handleMissing = (op: GqlMissing): Missing => ({
92
- // operator: "missing",
93
- // field: op.content.field,
94
- // });
95
- // handleExists = (op: GqlExists): Exists => ({
96
- // operator: "exists",
97
- // field: op.content.field,
98
- // });
99
- this.handleIncludes = (op)=>({
100
- "in": {
101
- [op.content.field]: op.content.value
102
- }
103
- });
104
- this.handleExcludes = (op)=>({
105
- "exclude": {
106
- [op.content.field]: op.content.value
107
- }
108
- });
109
- this.handleExcludeIfAny = (op)=>({
110
- "excludeifany": {
111
- [op.content.field]: op.content.value instanceof Array ? op.content.value : [
112
- op.content.value
113
- ]
114
- }
115
- });
116
- this.handleIntersection = (op)=>({
117
- and: op.content.map(convertGDCFilterToGen3Filter)
118
- });
119
- this.handleUnion = (op)=>({
120
- or: op.content.map(convertGDCFilterToGen3Filter)
121
- });
122
- }
123
- }
124
- const convertGDCFilterToGen3Filter = (gqlFilter)=>{
125
- const handler = new FromGDCToGen3();
126
- return handleGqlOperation(handler, gqlFilter);
127
- };
128
-
129
- const main = async ()=>{
130
- console.log('Starting');
131
- try {
132
- const { query } = getArgs({
133
- query: ''
134
- });
135
- if (!query) {
136
- console.log('No query provided');
137
- return;
138
- }
139
- console.log('query', query);
140
- const gdcQuery = JSON.parse(query);
141
- console.log(gdcQuery);
142
- const gen3Query = convertGDCFilterToGen3Filter(gdcQuery);
143
- console.log(gen3Query);
144
- } catch (e) {
145
- console.log('Invalid query');
146
- }
147
- };
148
- main();