@frontstackdev/cli 0.0.0-canary-20240830084913 → 0.0.0-canary-20240831065733
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.
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated using the
|
|
3
|
+
* Frontstack CLI, please do not edit it manually!
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ofetch } from 'ofetch'
|
|
7
|
+
import type { BlockEndpoints, BlockParameters, BlockResponses, RequestOptions, BlockQueryFilters, BlockMode, Page } from './generated-types'
|
|
8
|
+
import type { Query } from './query-types'
|
|
9
|
+
|
|
10
|
+
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
11
|
+
|
|
12
|
+
const endpoints: BlockEndpoints = {
|
|
13
|
+
{{#each paths}}
|
|
14
|
+
{{#if (endsWith this.get.operationId "Block")}}
|
|
15
|
+
{{this.summary}}: '{{@key}}',
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{/each}}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const invoke = (path: string, method: Method, payload: any, headers: any) => {
|
|
21
|
+
const _headers: any = {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Accept': 'application/json',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Merge headers
|
|
27
|
+
headers = { ..._headers, ...headers }
|
|
28
|
+
|
|
29
|
+
let options: any = {
|
|
30
|
+
method,
|
|
31
|
+
headers,
|
|
32
|
+
body: payload
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return ofetch(path, options)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Listing servers, even though only the first one is ever used */
|
|
39
|
+
const servers = {
|
|
40
|
+
{{#each servers}}
|
|
41
|
+
{{@key}}: {
|
|
42
|
+
description: '{{description}}',
|
|
43
|
+
url: '{{url}}'
|
|
44
|
+
},
|
|
45
|
+
{{/each}}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Frontstack Client
|
|
50
|
+
*/
|
|
51
|
+
const client = {
|
|
52
|
+
/**
|
|
53
|
+
* Fetch a block with parameters
|
|
54
|
+
*
|
|
55
|
+
* @param blockName The name of the block to fetch
|
|
56
|
+
* @example 'VariantCard'
|
|
57
|
+
* @param blockParameters The parameters to pass to the block
|
|
58
|
+
* @example { key: '<variant-id>' }
|
|
59
|
+
* @param config Further configuration options
|
|
60
|
+
* @example { query: { filter: { type: 'equals', field: 'name', value: 'Red Dress' } } }
|
|
61
|
+
*/
|
|
62
|
+
block: <BlockName extends keyof BlockEndpoints>(
|
|
63
|
+
blockName: BlockName,
|
|
64
|
+
blockParameters: BlockParameters[BlockName],
|
|
65
|
+
config?: (
|
|
66
|
+
BlockMode[BlockName] extends 'query' ?
|
|
67
|
+
{
|
|
68
|
+
/**
|
|
69
|
+
* Query parameters to pass to the block:
|
|
70
|
+
* - `filter` accepts an array of filter objects to filter results
|
|
71
|
+
* - `sort` accepts an array of sorting options to sort results
|
|
72
|
+
* - `search` to perform a text search
|
|
73
|
+
* - `limit` to limit the number of results returned
|
|
74
|
+
* - `page` to paginate results
|
|
75
|
+
*/
|
|
76
|
+
query?: Query<BlockQueryFilters[BlockName]>
|
|
77
|
+
} : {}
|
|
78
|
+
) &
|
|
79
|
+
{
|
|
80
|
+
/**
|
|
81
|
+
* Additional configuration options:
|
|
82
|
+
* - `requestUrl` to provide a URL that will be used to track the origin of the request
|
|
83
|
+
*/
|
|
84
|
+
options?: BlockOptions
|
|
85
|
+
}
|
|
86
|
+
): Promise<BlockResponses[BlockName]> => {
|
|
87
|
+
let endpoint: string = `${servers[0].url}${endpoints[blockName]}`;
|
|
88
|
+
|
|
89
|
+
// If payload contains `key` replace '{key}' in the endpoint
|
|
90
|
+
if(blockParameters.key) {
|
|
91
|
+
endpoint = endpoint.replace('{key}', blockParameters.key)
|
|
92
|
+
delete blockParameters.key
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Merge block parameters with query
|
|
96
|
+
let payload = { ...blockParameters, ...config?.query }
|
|
97
|
+
|
|
98
|
+
let headers: Record<string, string> = {}
|
|
99
|
+
|
|
100
|
+
if(config?.options?.requestUrl !== undefined) {
|
|
101
|
+
headers['fs-request-url'] = config.options.requestUrl
|
|
102
|
+
}
|
|
103
|
+
if (config?.options?.contextKey !== undefined) {
|
|
104
|
+
headers["fs-token"] = config.options.contextKey;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return invoke(endpoint, 'POST', payload, headers)
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Fetch a page by its slug
|
|
112
|
+
* @param slug The URL slug of the page (without protocol)
|
|
113
|
+
* @example my-brand.com/uk/women/shoes/running
|
|
114
|
+
*
|
|
115
|
+
* @returns The page data
|
|
116
|
+
* @example { data: { title: 'Running Shoes' }, type: 'ProductCategory', urls: [] }
|
|
117
|
+
*/
|
|
118
|
+
page: async (
|
|
119
|
+
slug: string | string[],
|
|
120
|
+
config?: {
|
|
121
|
+
options?: RequestOptions
|
|
122
|
+
}
|
|
123
|
+
): Promise<Page> => {
|
|
124
|
+
let endpoint: string = `${servers[0].url}/page/${slug}`;
|
|
125
|
+
|
|
126
|
+
let headers: Record<string, string> = {}
|
|
127
|
+
|
|
128
|
+
if(config?.options?.requestUrl !== undefined) {
|
|
129
|
+
headers['fs-request-url'] = config.options.requestUrl
|
|
130
|
+
}
|
|
131
|
+
if (config?.options?.contextKey !== undefined) {
|
|
132
|
+
headers["fs-token"] = config.options.contextKey;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return invoke(endpoint, 'POST', {}, headers)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default client
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated using the
|
|
3
|
+
* Frontstack CLI, please do not edit it manually!
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type Filter<T> = EqualsFilter<T> | RangeFilter<T> | ContainsFilter<T> | LogicalFilter<T>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper Types
|
|
10
|
+
*/
|
|
11
|
+
type NumericKeys<T> = {
|
|
12
|
+
[K in keyof T]: T[K] extends number ? K : never
|
|
13
|
+
}[keyof T];
|
|
14
|
+
|
|
15
|
+
type EnumKeys<T> = {
|
|
16
|
+
[K in keyof T]: T[K] extends string ? (string extends T[K] ? never : K) : never
|
|
17
|
+
}[keyof T];
|
|
18
|
+
|
|
19
|
+
// All string keys that are not enums
|
|
20
|
+
type StringKeys<T> = {
|
|
21
|
+
[K in keyof T]: T[K] extends string ? K : never
|
|
22
|
+
}[keyof T] & Exclude<keyof T, EnumKeys<T>>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Filter a field by equality. You can use an array of values to filter by multiple values
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* {
|
|
29
|
+
* type: 'equals',
|
|
30
|
+
* field: 'name',
|
|
31
|
+
* value: ['Red Dress', 'Blue Dress']
|
|
32
|
+
* }
|
|
33
|
+
*/
|
|
34
|
+
export type EqualsFilter<T> = {
|
|
35
|
+
[K in keyof T]: {
|
|
36
|
+
type: 'equals';
|
|
37
|
+
field: K;
|
|
38
|
+
value: T[K] | T[K][];
|
|
39
|
+
}
|
|
40
|
+
}[keyof T];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Filter a field by a range. You can use either from, to or both
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* {
|
|
47
|
+
* type: 'range',
|
|
48
|
+
* field: 'price',
|
|
49
|
+
* from: 10,
|
|
50
|
+
* to: 100
|
|
51
|
+
* }
|
|
52
|
+
*/
|
|
53
|
+
export type RangeFilter<T> = {
|
|
54
|
+
[K in NumericKeys<T>]: {
|
|
55
|
+
type: 'range';
|
|
56
|
+
field: K;
|
|
57
|
+
from?: T[K];
|
|
58
|
+
to?: T[K];
|
|
59
|
+
}
|
|
60
|
+
}[NumericKeys<T>];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Filter a field by a substring match.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* {
|
|
67
|
+
* type: 'contains',
|
|
68
|
+
* field: 'name',
|
|
69
|
+
* value: 'Dress'
|
|
70
|
+
* }
|
|
71
|
+
*/
|
|
72
|
+
export type ContainsFilter<T> = {
|
|
73
|
+
[K in StringKeys<T>]: {
|
|
74
|
+
type: 'contains';
|
|
75
|
+
field: K;
|
|
76
|
+
value: string;
|
|
77
|
+
}
|
|
78
|
+
}[StringKeys<T>];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Combine multiple filters with a logical operator
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* {
|
|
85
|
+
* type: 'and',
|
|
86
|
+
* filters: [
|
|
87
|
+
* {
|
|
88
|
+
* type: 'equals',
|
|
89
|
+
* field: 'name',
|
|
90
|
+
* value: 'Red Dress'
|
|
91
|
+
* },
|
|
92
|
+
* {
|
|
93
|
+
* type: 'range',
|
|
94
|
+
* field: 'price',
|
|
95
|
+
* from: 10,
|
|
96
|
+
* to: 100
|
|
97
|
+
* }
|
|
98
|
+
* ]
|
|
99
|
+
* }
|
|
100
|
+
*/
|
|
101
|
+
export type LogicalFilter<T> = {
|
|
102
|
+
type: 'and' | 'or';
|
|
103
|
+
filters: Filter<T>[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type SortField<T> = keyof T;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Sort results by a field and direction
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* {
|
|
113
|
+
* field: 'price',
|
|
114
|
+
* direction: 'asc'
|
|
115
|
+
* }
|
|
116
|
+
*/
|
|
117
|
+
export type Sort<T> = {
|
|
118
|
+
field: SortField<T >;
|
|
119
|
+
direction: 'asc' | 'desc';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
export type Query<T> = {
|
|
124
|
+
/**
|
|
125
|
+
* Filter results by a set of conditions
|
|
126
|
+
*/
|
|
127
|
+
filter?: Filter<T>[];
|
|
128
|
+
/**
|
|
129
|
+
* Will search fields that are marked as searchable
|
|
130
|
+
*/
|
|
131
|
+
search?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Sort results by a field and direction
|
|
134
|
+
*
|
|
135
|
+
* There are three ways to sort:
|
|
136
|
+
*
|
|
137
|
+
* * A single sort object
|
|
138
|
+
* * An array of sort objects
|
|
139
|
+
* * A single sort field
|
|
140
|
+
*/
|
|
141
|
+
sort?: Sort<T> | Sort<T>[] | SortField<T>;
|
|
142
|
+
/**
|
|
143
|
+
* Limit the number of results returned
|
|
144
|
+
*/
|
|
145
|
+
limit?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Paginate results
|
|
148
|
+
*/
|
|
149
|
+
page?: number;
|
|
150
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated using the
|
|
3
|
+
* Frontstack CLI, please do not edit it manually!
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { components } from './fetch-api.d.ts'
|
|
7
|
+
|
|
8
|
+
/* Frontstack default types */
|
|
9
|
+
|
|
10
|
+
type SearchQuery = components['schemas']['SearchQuery']
|
|
11
|
+
type Media = components['schemas']['Media']
|
|
12
|
+
|
|
13
|
+
type RequestOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* @description URL to the current page
|
|
16
|
+
*
|
|
17
|
+
* Optionally, Frontstack accepts a URL to the current page. This is useful for tracking purposes.
|
|
18
|
+
*/
|
|
19
|
+
requestUrl?: string
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @description Context key
|
|
23
|
+
*
|
|
24
|
+
* Providing a context key will override the default context key for the request
|
|
25
|
+
*/
|
|
26
|
+
contextKey?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* List of all types used to fetch block parameters */
|
|
30
|
+
|
|
31
|
+
{{#each paths}}{{#if (endsWith this.get.operationId "Block")}}export type {{this.summary}}Parameters = {
|
|
32
|
+
{{#each this.get.parameters}}
|
|
33
|
+
{{#if @first}}{{else}}
|
|
34
|
+
{{/if}}
|
|
35
|
+
/**
|
|
36
|
+
* @description {{description}}
|
|
37
|
+
*/
|
|
38
|
+
{{name}}{{#unless required}}?{{/unless}}: {{{getSchemaType schema}}};
|
|
39
|
+
{{/each}}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
{{/if}}{{/each}}
|
|
43
|
+
/* List of all blocks, used for IDE autocompletion */
|
|
44
|
+
|
|
45
|
+
type BlockParameters = {
|
|
46
|
+
{{#each components.schemas}}
|
|
47
|
+
{{#if (contains tags "blocks")}}
|
|
48
|
+
{{@key}}: {{@key}}Parameters
|
|
49
|
+
{{/if}}
|
|
50
|
+
{{/each}}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type BlockEndpoints = {
|
|
54
|
+
[key in keyof BlockParameters]: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare global {
|
|
58
|
+
{{#each components.schemas}}
|
|
59
|
+
{{#if (contains tags "blocks")}}
|
|
60
|
+
export type {{@key}} = {
|
|
61
|
+
{{#each properties}}
|
|
62
|
+
{{@key}}: {{{getSchemaType this}}}
|
|
63
|
+
{{/each}}
|
|
64
|
+
}
|
|
65
|
+
{{/if}}
|
|
66
|
+
{{/each}}
|
|
67
|
+
}
|
|
68
|
+
{{!--
|
|
69
|
+
|
|
70
|
+
The loop below would work as well, but it makes all parameters optional, which leads to bad DX
|
|
71
|
+
Solve later
|
|
72
|
+
|
|
73
|
+
{{#each components.schemas}}
|
|
74
|
+
{{#if (contains tags "blocks")}}
|
|
75
|
+
export type {{@key}} = components['schemas']['{{@key}}']
|
|
76
|
+
{{/if}}
|
|
77
|
+
{{/each}}
|
|
78
|
+
--}}
|
|
79
|
+
type BlockResponses = {
|
|
80
|
+
{{#each components.schemas}}
|
|
81
|
+
{{#if (contains tags "blocks")}}
|
|
82
|
+
{{@key}}: {{@key}}
|
|
83
|
+
{{/if}}
|
|
84
|
+
{{/each}}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type BlockQueryFilters = {
|
|
88
|
+
{{#each components.schemas}}
|
|
89
|
+
{{#if (contains tags "blocks")}}
|
|
90
|
+
{{@key}}: components['schemas']['{{@key}}QueryOptions']['filter']
|
|
91
|
+
{{/if}}
|
|
92
|
+
{{/each}}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type BlockMode = {
|
|
96
|
+
{{#each components.schemas}}
|
|
97
|
+
{{#if (contains tags "blocks")}}
|
|
98
|
+
{{@key}}: {{#if (contains tags "query-block") }}'query'{{else}}'key'{{/if}};
|
|
99
|
+
{{/if}}
|
|
100
|
+
{{/each}}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type Page = components['schemas']['Page']
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontstackdev/cli",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20240831065733",
|
|
4
4
|
"description": "Frontstack CLI for managing projects",
|
|
5
5
|
"exports": "./dist/frontstack.mjs",
|
|
6
6
|
"module": "./dist/frontstack.mjs",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "pkgroll --clean-dist",
|
|
9
|
+
"build": "pkgroll --clean-dist --minify && mkdir -p dist/src/commands/generate && cp -r src/commands/generate/templates dist/src/commands/generate/",
|
|
10
10
|
"start": "tsx src/frontstack.ts",
|
|
11
11
|
"frontstack": "pnpm build && node dist/frontstack.mjs",
|
|
12
12
|
"test": "vitest",
|
|
13
|
-
"lint": "tsc"
|
|
13
|
+
"lint": "tsc",
|
|
14
|
+
"clean": "rm -rf dist"
|
|
14
15
|
},
|
|
15
16
|
"keywords": [],
|
|
16
17
|
"bin": {
|