@wp-typia/project-tools 0.15.4 → 0.16.1
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.
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +23 -2
- package/dist/runtime/scaffold-onboarding.d.ts +2 -1
- package/dist/runtime/scaffold-onboarding.js +37 -8
- package/dist/runtime/scaffold.js +26 -15
- package/package.json +2 -2
- package/templates/_shared/base/package.json.mustache +4 -3
- package/templates/_shared/base/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/core/package.json.mustache +4 -3
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +42 -28
- package/templates/_shared/compound/core/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/persistence/package.json.mustache +4 -3
- package/templates/_shared/compound/persistence/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +28 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +8 -0
- package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +5 -5
- package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +5 -5
- package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +5 -5
- package/templates/_shared/persistence/core/package.json.mustache +4 -3
- package/templates/_shared/persistence/core/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +28 -0
- package/templates/_shared/persistence/core/src/interactivity.ts.mustache +5 -0
- package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +5 -5
- package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +5 -5
- package/templates/_shared/workspace/persistence-auth/server.php.mustache +7 -5
- package/templates/_shared/workspace/persistence-public/server.php.mustache +7 -5
- package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +5 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +10 -3
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +5 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +10 -3
- package/templates/interactivity/package.json.mustache +4 -3
- package/templates/interactivity/src/edit.tsx.mustache +1 -1
- package/templates/interactivity/src/save.tsx.mustache +1 -1
- package/templates/persistence/src/edit.tsx.mustache +2 -2
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"add-child": "tsx scripts/add-compound-child.ts",
|
|
11
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
11
12
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
12
13
|
"sync-rest": "tsx scripts/sync-rest-contracts.ts",
|
|
13
|
-
"build": "bun run sync
|
|
14
|
-
"start": "bun run sync
|
|
14
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
15
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
15
16
|
"dev": "bun run start",
|
|
16
|
-
"typecheck": "bun run sync
|
|
17
|
+
"typecheck": "bun run sync --check && tsc --noEmit",
|
|
17
18
|
"lint:js": "wp-scripts lint-js",
|
|
18
19
|
"lint:css": "wp-scripts lint-style",
|
|
19
20
|
"lint": "bun run lint:js && bun run lint:css",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
runSyncBlockMetadata,
|
|
5
6
|
syncEndpointClient,
|
|
6
7
|
syncRestOpenApi,
|
|
7
8
|
syncTypeSchemas,
|
|
@@ -26,6 +27,31 @@ function parseCliOptions( argv: string[] ) {
|
|
|
26
27
|
return options;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
async function assertTypeArtifactsCurrent( block: ( typeof BLOCKS )[ number ] ) {
|
|
31
|
+
const report = await runSyncBlockMetadata( {
|
|
32
|
+
blockJsonFile: path.join( 'src', 'blocks', block.slug, 'block.json' ),
|
|
33
|
+
jsonSchemaFile: path.join( 'src', 'blocks', block.slug, 'typia.schema.json' ),
|
|
34
|
+
manifestFile: path.join( 'src', 'blocks', block.slug, 'typia.manifest.json' ),
|
|
35
|
+
openApiFile: path.join( 'src', 'blocks', block.slug, 'typia.openapi.json' ),
|
|
36
|
+
sourceTypeName: block.attributeTypeName,
|
|
37
|
+
typesFile: block.typesFile,
|
|
38
|
+
}, {
|
|
39
|
+
check: true,
|
|
40
|
+
} );
|
|
41
|
+
|
|
42
|
+
if ( report.failure?.code === 'stale-generated-artifact' ) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`${ block.slug }: ${ report.failure.message }\nRun \`sync\` or \`sync-types\` first to refresh type-derived artifacts before rerunning \`sync-rest\`.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if ( report.failure ) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`${ block.slug }: type-derived artifact preflight failed before sync-rest.\n${ report.failure.message }`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
async function main() {
|
|
30
56
|
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
31
57
|
|
|
@@ -39,6 +65,8 @@ async function main() {
|
|
|
39
65
|
continue;
|
|
40
66
|
}
|
|
41
67
|
|
|
68
|
+
await assertTypeArtifactsCurrent( block );
|
|
69
|
+
|
|
42
70
|
for ( const [ baseName, contract ] of Object.entries( block.restManifest.contracts ) ) {
|
|
43
71
|
await syncTypeSchemas( {
|
|
44
72
|
jsonSchemaFile: path.join(
|
|
@@ -123,6 +123,8 @@ const { actions, state } = store( '{{slugKebabCase}}', {
|
|
|
123
123
|
let bootstrapSucceeded = false;
|
|
124
124
|
let lastBootstrapError =
|
|
125
125
|
'Unable to initialize write access';
|
|
126
|
+
const includePublicWriteCredentials = {{isPublicPersistencePolicy}};
|
|
127
|
+
const includeRestNonce = {{isAuthenticatedPersistencePolicy}};
|
|
126
128
|
|
|
127
129
|
for ( let attempt = 1; attempt <= BOOTSTRAP_MAX_ATTEMPTS; attempt += 1 ) {
|
|
128
130
|
try {
|
|
@@ -146,16 +148,22 @@ const { actions, state } = store( '{{slugKebabCase}}', {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
clientState.writeExpiry =
|
|
151
|
+
includePublicWriteCredentials &&
|
|
152
|
+
'publicWriteExpiresAt' in result.data &&
|
|
149
153
|
typeof result.data.publicWriteExpiresAt === 'number' &&
|
|
150
154
|
result.data.publicWriteExpiresAt > 0
|
|
151
155
|
? result.data.publicWriteExpiresAt
|
|
152
156
|
: 0;
|
|
153
157
|
clientState.writeToken =
|
|
158
|
+
includePublicWriteCredentials &&
|
|
159
|
+
'publicWriteToken' in result.data &&
|
|
154
160
|
typeof result.data.publicWriteToken === 'string' &&
|
|
155
161
|
result.data.publicWriteToken.length > 0
|
|
156
162
|
? result.data.publicWriteToken
|
|
157
163
|
: '';
|
|
158
164
|
clientState.writeNonce =
|
|
165
|
+
includeRestNonce &&
|
|
166
|
+
'restNonce' in result.data &&
|
|
159
167
|
typeof result.data.restNonce === 'string' &&
|
|
160
168
|
result.data.restNonce.length > 0
|
|
161
169
|
? result.data.restNonce
|
|
@@ -79,7 +79,7 @@ function {{phpPrefix}}_with_counter_lock( $post_id, $resource_key, $callback ) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
function {{phpPrefix}}_maybe_install_storage() {
|
|
82
|
-
if ( 'custom-table' !==
|
|
82
|
+
if ( 'custom-table' !== {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -110,7 +110,7 @@ function {{phpPrefix}}_maybe_install_storage() {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function {{phpPrefix}}_ensure_storage_installed() {
|
|
113
|
-
if ( 'custom-table' ===
|
|
113
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE && '1.0.0' !== get_option( '{{phpPrefix}}_storage_version', '' ) ) {
|
|
114
114
|
{{phpPrefix}}_maybe_install_storage();
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -122,7 +122,7 @@ function {{phpPrefix}}_get_rest_build_dir() {
|
|
|
122
122
|
function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
123
123
|
global $wpdb;
|
|
124
124
|
|
|
125
|
-
if ( 'custom-table' ===
|
|
125
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
126
126
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
127
127
|
$count = $wpdb->get_var(
|
|
128
128
|
$wpdb->prepare(
|
|
@@ -144,7 +144,7 @@ function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
|
144
144
|
function {{phpPrefix}}_increment_counter( $post_id, $resource_key, $delta ) {
|
|
145
145
|
global $wpdb;
|
|
146
146
|
|
|
147
|
-
if ( 'custom-table' ===
|
|
147
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
148
148
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
149
149
|
$delta_value = (int) $delta;
|
|
150
150
|
$initial_count = max( 0, $delta_value );
|
|
@@ -188,7 +188,7 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
|
|
|
188
188
|
'postId' => (int) $post_id,
|
|
189
189
|
'resourceKey' => (string) $resource_key,
|
|
190
190
|
'count' => (int) $count,
|
|
191
|
-
'storage' =>
|
|
191
|
+
'storage' => {{phpPrefixUpper}}_DATA_STORAGE_MODE,
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -82,7 +82,7 @@ function {{phpPrefix}}_with_counter_lock( $post_id, $resource_key, $callback ) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function {{phpPrefix}}_maybe_install_storage() {
|
|
85
|
-
if ( 'custom-table' !==
|
|
85
|
+
if ( 'custom-table' !== {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -113,7 +113,7 @@ function {{phpPrefix}}_maybe_install_storage() {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
function {{phpPrefix}}_ensure_storage_installed() {
|
|
116
|
-
if ( 'custom-table' ===
|
|
116
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE && '1.0.0' !== get_option( '{{phpPrefix}}_storage_version', '' ) ) {
|
|
117
117
|
{{phpPrefix}}_maybe_install_storage();
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -125,7 +125,7 @@ function {{phpPrefix}}_get_rest_build_dir() {
|
|
|
125
125
|
function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
126
126
|
global $wpdb;
|
|
127
127
|
|
|
128
|
-
if ( 'custom-table' ===
|
|
128
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
129
129
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
130
130
|
$count = $wpdb->get_var(
|
|
131
131
|
$wpdb->prepare(
|
|
@@ -147,7 +147,7 @@ function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
|
147
147
|
function {{phpPrefix}}_increment_counter( $post_id, $resource_key, $delta ) {
|
|
148
148
|
global $wpdb;
|
|
149
149
|
|
|
150
|
-
if ( 'custom-table' ===
|
|
150
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
151
151
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
152
152
|
$delta_value = (int) $delta;
|
|
153
153
|
$initial_count = max( 0, $delta_value );
|
|
@@ -191,7 +191,7 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
|
|
|
191
191
|
'postId' => (int) $post_id,
|
|
192
192
|
'resourceKey' => (string) $resource_key,
|
|
193
193
|
'count' => (int) $count,
|
|
194
|
-
'storage' =>
|
|
194
|
+
'storage' => {{phpPrefixUpper}}_DATA_STORAGE_MODE,
|
|
195
195
|
);
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -84,7 +84,7 @@ function {{phpPrefix}}_with_counter_lock( $post_id, $resource_key, $callback ) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function {{phpPrefix}}_maybe_install_storage() {
|
|
87
|
-
if ( 'custom-table' !==
|
|
87
|
+
if ( 'custom-table' !== {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -115,7 +115,7 @@ function {{phpPrefix}}_maybe_install_storage() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
function {{phpPrefix}}_ensure_storage_installed() {
|
|
118
|
-
if ( 'custom-table' ===
|
|
118
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE && '1.0.0' !== get_option( '{{phpPrefix}}_storage_version', '' ) ) {
|
|
119
119
|
{{phpPrefix}}_maybe_install_storage();
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -127,7 +127,7 @@ function {{phpPrefix}}_get_rest_build_dir() {
|
|
|
127
127
|
function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
128
128
|
global $wpdb;
|
|
129
129
|
|
|
130
|
-
if ( 'custom-table' ===
|
|
130
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
131
131
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
132
132
|
$count = $wpdb->get_var(
|
|
133
133
|
$wpdb->prepare(
|
|
@@ -149,7 +149,7 @@ function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
|
149
149
|
function {{phpPrefix}}_increment_counter( $post_id, $resource_key, $delta ) {
|
|
150
150
|
global $wpdb;
|
|
151
151
|
|
|
152
|
-
if ( 'custom-table' ===
|
|
152
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
153
153
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
154
154
|
$delta_value = (int) $delta;
|
|
155
155
|
$initial_count = max( 0, $delta_value );
|
|
@@ -193,7 +193,7 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
|
|
|
193
193
|
'postId' => (int) $post_id,
|
|
194
194
|
'resourceKey' => (string) $resource_key,
|
|
195
195
|
'count' => (int) $count,
|
|
196
|
-
'storage' =>
|
|
196
|
+
'storage' => {{phpPrefixUpper}}_DATA_STORAGE_MODE,
|
|
197
197
|
);
|
|
198
198
|
}
|
|
199
199
|
|
|
@@ -7,16 +7,17 @@
|
|
|
7
7
|
"license": "GPL-2.0-or-later",
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
10
11
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
12
|
"sync-rest": "tsx scripts/sync-rest-contracts.ts",
|
|
12
|
-
"build": "bun run sync
|
|
13
|
-
"start": "bun run sync
|
|
13
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
14
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
14
15
|
"dev": "bun run start",
|
|
15
16
|
"lint:js": "wp-scripts lint-js",
|
|
16
17
|
"lint:css": "wp-scripts lint-style",
|
|
17
18
|
"lint": "bun run lint:js && bun run lint:css",
|
|
18
19
|
"format": "wp-scripts format",
|
|
19
|
-
"typecheck": "bun run sync
|
|
20
|
+
"typecheck": "bun run sync --check && tsc --noEmit"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@wp-typia/block-runtime": "{{blockRuntimePackageVersion}}",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import {
|
|
3
3
|
defineEndpointManifest,
|
|
4
|
+
runSyncBlockMetadata,
|
|
4
5
|
syncEndpointClient,
|
|
5
6
|
syncRestOpenApi,
|
|
6
7
|
syncTypeSchemas,
|
|
@@ -23,6 +24,31 @@ function parseCliOptions( argv: string[] ) {
|
|
|
23
24
|
return options;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
async function assertTypeArtifactsCurrent() {
|
|
28
|
+
const report = await runSyncBlockMetadata( {
|
|
29
|
+
blockJsonFile: 'src/block.json',
|
|
30
|
+
jsonSchemaFile: 'src/typia.schema.json',
|
|
31
|
+
manifestFile: 'src/typia.manifest.json',
|
|
32
|
+
openApiFile: 'src/typia.openapi.json',
|
|
33
|
+
sourceTypeName: '{{pascalCase}}Attributes',
|
|
34
|
+
typesFile: 'src/types.ts',
|
|
35
|
+
}, {
|
|
36
|
+
check: true,
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
if ( report.failure?.code === 'stale-generated-artifact' ) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`${ report.failure.message }\nRun \`sync\` or \`sync-types\` first to refresh type-derived artifacts before rerunning \`sync-rest\`.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( report.failure ) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Type-derived artifact preflight failed before sync-rest.\n${ report.failure.message }`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
const REST_ENDPOINT_MANIFEST = defineEndpointManifest( {
|
|
27
53
|
contracts: {
|
|
28
54
|
'state-query': {
|
|
@@ -85,6 +111,8 @@ const REST_ENDPOINT_MANIFEST = defineEndpointManifest( {
|
|
|
85
111
|
async function main() {
|
|
86
112
|
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
87
113
|
|
|
114
|
+
await assertTypeArtifactsCurrent();
|
|
115
|
+
|
|
88
116
|
for ( const [ baseName, contract ] of Object.entries( REST_ENDPOINT_MANIFEST.contracts ) ) {
|
|
89
117
|
await syncTypeSchemas( {
|
|
90
118
|
jsonSchemaFile: `src/api-schemas/${ baseName }.schema.json`,
|
|
@@ -126,6 +126,7 @@ const { actions, state } = store( '{{slugKebabCase}}', {
|
|
|
126
126
|
let bootstrapSucceeded = false;
|
|
127
127
|
let lastBootstrapError =
|
|
128
128
|
'Unable to initialize write access';
|
|
129
|
+
const includePublicWriteCredentials = {{isPublicPersistencePolicy}};
|
|
129
130
|
const includeRestNonce = {{isAuthenticatedPersistencePolicy}};
|
|
130
131
|
|
|
131
132
|
for ( let attempt = 1; attempt <= BOOTSTRAP_MAX_ATTEMPTS; attempt += 1 ) {
|
|
@@ -150,11 +151,15 @@ const { actions, state } = store( '{{slugKebabCase}}', {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
clientState.writeExpiry =
|
|
154
|
+
includePublicWriteCredentials &&
|
|
155
|
+
'publicWriteExpiresAt' in result.data &&
|
|
153
156
|
typeof result.data.publicWriteExpiresAt === 'number' &&
|
|
154
157
|
result.data.publicWriteExpiresAt > 0
|
|
155
158
|
? result.data.publicWriteExpiresAt
|
|
156
159
|
: 0;
|
|
157
160
|
clientState.writeToken =
|
|
161
|
+
includePublicWriteCredentials &&
|
|
162
|
+
'publicWriteToken' in result.data &&
|
|
158
163
|
typeof result.data.publicWriteToken === 'string' &&
|
|
159
164
|
result.data.publicWriteToken.length > 0
|
|
160
165
|
? result.data.publicWriteToken
|
|
@@ -81,7 +81,7 @@ function {{phpPrefix}}_with_counter_lock( $post_id, $resource_key, $callback ) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function {{phpPrefix}}_maybe_install_storage() {
|
|
84
|
-
if ( 'custom-table' !==
|
|
84
|
+
if ( 'custom-table' !== {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -112,7 +112,7 @@ function {{phpPrefix}}_maybe_install_storage() {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function {{phpPrefix}}_ensure_storage_installed() {
|
|
115
|
-
if ( 'custom-table' ===
|
|
115
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE && '1.0.0' !== get_option( '{{phpPrefix}}_storage_version', '' ) ) {
|
|
116
116
|
{{phpPrefix}}_maybe_install_storage();
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -170,7 +170,7 @@ function {{phpPrefix}}_validate_and_sanitize_request( $value, $schema_name, $par
|
|
|
170
170
|
function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
171
171
|
global $wpdb;
|
|
172
172
|
|
|
173
|
-
if ( 'custom-table' ===
|
|
173
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
174
174
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
175
175
|
$count = $wpdb->get_var(
|
|
176
176
|
$wpdb->prepare(
|
|
@@ -191,7 +191,7 @@ function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
|
|
|
191
191
|
function {{phpPrefix}}_increment_counter( $post_id, $resource_key, $delta ) {
|
|
192
192
|
global $wpdb;
|
|
193
193
|
|
|
194
|
-
if ( 'custom-table' ===
|
|
194
|
+
if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
|
|
195
195
|
$table_name = {{phpPrefix}}_get_counter_table_name();
|
|
196
196
|
$delta_value = (int) $delta;
|
|
197
197
|
$initial_count = max( 0, $delta_value );
|
|
@@ -235,7 +235,7 @@ function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
|
|
|
235
235
|
'postId' => (int) $post_id,
|
|
236
236
|
'resourceKey' => (string) $resource_key,
|
|
237
237
|
'count' => (int) $count,
|
|
238
|
-
'storage' =>
|
|
238
|
+
'storage' => {{phpPrefixUpper}}_DATA_STORAGE_MODE,
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
241
|
|