@pgpmjs/core 6.31.0 → 6.32.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.
- package/core/class/pgpm.d.ts +27 -2
- package/core/class/pgpm.js +103 -22
- package/esm/core/class/pgpm.js +103 -22
- package/package.json +6 -6
package/core/class/pgpm.d.ts
CHANGED
|
@@ -137,10 +137,35 @@ export declare class PgpmPackage {
|
|
|
137
137
|
private createSqlFiles;
|
|
138
138
|
publishToDist(distFolder?: string): void;
|
|
139
139
|
/**
|
|
140
|
-
* Installs an extension npm package into the
|
|
141
|
-
*
|
|
140
|
+
* Installs an extension npm package into the workspace extensions directory.
|
|
141
|
+
*
|
|
142
|
+
* When run inside a module, the installed version is recorded in the
|
|
143
|
+
* module's package.json dependencies and its .control requires list.
|
|
144
|
+
* When run at the workspace root, the installed version is recorded in
|
|
145
|
+
* the workspace pgpm.json `dependencies` field instead — no .control
|
|
146
|
+
* file is needed.
|
|
142
147
|
*/
|
|
143
148
|
installModules(...pkgstrs: string[]): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Returns the workspace-level pgpm module dependencies recorded in
|
|
151
|
+
* the workspace pgpm.json `dependencies` field.
|
|
152
|
+
*/
|
|
153
|
+
getWorkspaceDependencies(): Record<string, string>;
|
|
154
|
+
/**
|
|
155
|
+
* Records installed module versions into the workspace pgpm.json
|
|
156
|
+
* `dependencies` field. No-op when the workspace uses pgpm.config.js.
|
|
157
|
+
*/
|
|
158
|
+
recordWorkspaceDependencies(versions: Record<string, string>): void;
|
|
159
|
+
/**
|
|
160
|
+
* Installs the workspace-level dependencies declared in pgpm.json into
|
|
161
|
+
* the workspace extensions/ directory. Skips modules already present
|
|
162
|
+
* unless `force` is set.
|
|
163
|
+
*
|
|
164
|
+
* @returns The list of installed package specs (name@version)
|
|
165
|
+
*/
|
|
166
|
+
installWorkspaceDependencies(options?: {
|
|
167
|
+
force?: boolean;
|
|
168
|
+
}): Promise<string[]>;
|
|
144
169
|
/**
|
|
145
170
|
* Returns information about which of the specified modules are installed.
|
|
146
171
|
* Checks the module's package.json dependencies to determine installation status.
|
package/core/class/pgpm.js
CHANGED
|
@@ -236,7 +236,15 @@ class PgpmPackage {
|
|
|
236
236
|
listModules() {
|
|
237
237
|
if (!this.workspacePath)
|
|
238
238
|
return {};
|
|
239
|
-
|
|
239
|
+
// Workspace membership is first-class: modules are discovered from the
|
|
240
|
+
// workspace's declared `packages` globs plus the installed extensions/
|
|
241
|
+
// directory — never from an unscoped workspace-wide scan. This keeps
|
|
242
|
+
// nested workspaces (e.g. test fixtures) out of the module map.
|
|
243
|
+
const packageDirs = [
|
|
244
|
+
...this.allowedDirs,
|
|
245
|
+
...glob.sync(path_1.default.join(this.workspacePath, EXTENSIONS_DIR, '{*,@*/*}'))
|
|
246
|
+
];
|
|
247
|
+
const moduleFiles = [...new Set(packageDirs.flatMap(dir => glob.sync(`${dir}/**/*.control`)))].filter((file) => !/node_modules/.test(file)).sort((a, b) => a.localeCompare(b));
|
|
240
248
|
// Group files by module name to handle collisions
|
|
241
249
|
const filesByName = new Map();
|
|
242
250
|
moduleFiles.forEach((file) => {
|
|
@@ -790,21 +798,31 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
790
798
|
}
|
|
791
799
|
}
|
|
792
800
|
/**
|
|
793
|
-
* Installs an extension npm package into the
|
|
794
|
-
*
|
|
801
|
+
* Installs an extension npm package into the workspace extensions directory.
|
|
802
|
+
*
|
|
803
|
+
* When run inside a module, the installed version is recorded in the
|
|
804
|
+
* module's package.json dependencies and its .control requires list.
|
|
805
|
+
* When run at the workspace root, the installed version is recorded in
|
|
806
|
+
* the workspace pgpm.json `dependencies` field instead — no .control
|
|
807
|
+
* file is needed.
|
|
795
808
|
*/
|
|
796
809
|
async installModules(...pkgstrs) {
|
|
797
810
|
this.ensureWorkspace();
|
|
798
|
-
this.
|
|
811
|
+
const inModule = this.isInModule();
|
|
799
812
|
const originalDir = process.cwd();
|
|
800
813
|
const skitchExtDir = path_1.default.join(this.workspacePath, EXTENSIONS_DIR);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
814
|
+
let pkgJsonPath;
|
|
815
|
+
let pkgData;
|
|
816
|
+
if (inModule) {
|
|
817
|
+
pkgJsonPath = path_1.default.join(this.modulePath, 'package.json');
|
|
818
|
+
if (!fs_1.default.existsSync(pkgJsonPath)) {
|
|
819
|
+
throw new Error(`No package.json found at module path: ${this.modulePath}`);
|
|
820
|
+
}
|
|
821
|
+
pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
|
|
822
|
+
pkgData.dependencies = pkgData.dependencies || {};
|
|
804
823
|
}
|
|
805
|
-
const pkgData = JSON.parse(fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
|
|
806
|
-
pkgData.dependencies = pkgData.dependencies || {};
|
|
807
824
|
const newlyAdded = [];
|
|
825
|
+
const installedVersions = {};
|
|
808
826
|
for (const pkgstr of pkgstrs) {
|
|
809
827
|
const { name } = (0, parse_package_name_1.parse)(pkgstr);
|
|
810
828
|
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'pgpm-install-'));
|
|
@@ -837,7 +855,10 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
837
855
|
throw new Error(`Missing package.json in installed extension: ${dst}`);
|
|
838
856
|
}
|
|
839
857
|
const { version } = JSON.parse(fs_1.default.readFileSync(pkgJsonFile, 'utf-8'));
|
|
840
|
-
|
|
858
|
+
installedVersions[name] = `${version}`;
|
|
859
|
+
if (pkgData) {
|
|
860
|
+
pkgData.dependencies[name] = `${version}`;
|
|
861
|
+
}
|
|
841
862
|
const extensionName = (0, files_2.getExtensionName)(dst);
|
|
842
863
|
newlyAdded.push(extensionName);
|
|
843
864
|
}
|
|
@@ -847,20 +868,80 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
847
868
|
process.chdir(originalDir);
|
|
848
869
|
}
|
|
849
870
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
871
|
+
if (inModule && pkgData && pkgJsonPath) {
|
|
872
|
+
const { dependencies, devDependencies, ...rest } = pkgData;
|
|
873
|
+
const finalPkgData = { ...rest };
|
|
874
|
+
if (dependencies) {
|
|
875
|
+
finalPkgData.dependencies = sortObjectByKey(dependencies);
|
|
876
|
+
}
|
|
877
|
+
if (devDependencies) {
|
|
878
|
+
finalPkgData.devDependencies = sortObjectByKey(devDependencies);
|
|
879
|
+
}
|
|
880
|
+
fs_1.default.writeFileSync(pkgJsonPath, JSON.stringify(finalPkgData, null, 2));
|
|
881
|
+
logger.success(`📦 Updated package.json with: ${pkgstrs.join(', ')}`);
|
|
882
|
+
// ─── Update .control file with actual extension names ──────────────
|
|
883
|
+
const currentDeps = this.getRequiredModules();
|
|
884
|
+
const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
|
|
885
|
+
(0, files_2.writeExtensions)(this.modulePath, updatedDeps);
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
this.recordWorkspaceDependencies(installedVersions);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Returns the workspace-level pgpm module dependencies recorded in
|
|
893
|
+
* the workspace pgpm.json `dependencies` field.
|
|
894
|
+
*/
|
|
895
|
+
getWorkspaceDependencies() {
|
|
896
|
+
this.ensureWorkspace();
|
|
897
|
+
const configPath = path_1.default.join(this.workspacePath, 'pgpm.json');
|
|
898
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
899
|
+
return {};
|
|
900
|
+
}
|
|
901
|
+
const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
902
|
+
return config.dependencies || {};
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Records installed module versions into the workspace pgpm.json
|
|
906
|
+
* `dependencies` field. No-op when the workspace uses pgpm.config.js.
|
|
907
|
+
*/
|
|
908
|
+
recordWorkspaceDependencies(versions) {
|
|
909
|
+
this.ensureWorkspace();
|
|
910
|
+
if (Object.keys(versions).length === 0)
|
|
911
|
+
return;
|
|
912
|
+
const configPath = path_1.default.join(this.workspacePath, 'pgpm.json');
|
|
913
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
914
|
+
logger.warn('No pgpm.json found at workspace root — skipping dependency recording.');
|
|
915
|
+
return;
|
|
854
916
|
}
|
|
855
|
-
|
|
856
|
-
|
|
917
|
+
const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
918
|
+
config.dependencies = sortObjectByKey({
|
|
919
|
+
...(config.dependencies || {}),
|
|
920
|
+
...versions
|
|
921
|
+
});
|
|
922
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
923
|
+
logger.success(`📦 Updated pgpm.json dependencies with: ${Object.keys(versions).join(', ')}`);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Installs the workspace-level dependencies declared in pgpm.json into
|
|
927
|
+
* the workspace extensions/ directory. Skips modules already present
|
|
928
|
+
* unless `force` is set.
|
|
929
|
+
*
|
|
930
|
+
* @returns The list of installed package specs (name@version)
|
|
931
|
+
*/
|
|
932
|
+
async installWorkspaceDependencies(options) {
|
|
933
|
+
this.ensureWorkspace();
|
|
934
|
+
const { force = false } = options || {};
|
|
935
|
+
const dependencies = this.getWorkspaceDependencies();
|
|
936
|
+
const installedModules = this.getWorkspaceInstalledModules();
|
|
937
|
+
const toInstall = Object.entries(dependencies)
|
|
938
|
+
.filter(([name]) => force || !installedModules.includes(name))
|
|
939
|
+
.map(([name, version]) => `${name}@${version}`);
|
|
940
|
+
if (toInstall.length === 0) {
|
|
941
|
+
return [];
|
|
857
942
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
// ─── Update .control file with actual extension names ──────────────
|
|
861
|
-
const currentDeps = this.getRequiredModules();
|
|
862
|
-
const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
|
|
863
|
-
(0, files_2.writeExtensions)(this.modulePath, updatedDeps);
|
|
943
|
+
await this.installModules(...toInstall);
|
|
944
|
+
return toInstall;
|
|
864
945
|
}
|
|
865
946
|
/**
|
|
866
947
|
* Returns information about which of the specified modules are installed.
|
package/esm/core/class/pgpm.js
CHANGED
|
@@ -197,7 +197,15 @@ export class PgpmPackage {
|
|
|
197
197
|
listModules() {
|
|
198
198
|
if (!this.workspacePath)
|
|
199
199
|
return {};
|
|
200
|
-
|
|
200
|
+
// Workspace membership is first-class: modules are discovered from the
|
|
201
|
+
// workspace's declared `packages` globs plus the installed extensions/
|
|
202
|
+
// directory — never from an unscoped workspace-wide scan. This keeps
|
|
203
|
+
// nested workspaces (e.g. test fixtures) out of the module map.
|
|
204
|
+
const packageDirs = [
|
|
205
|
+
...this.allowedDirs,
|
|
206
|
+
...glob.sync(path.join(this.workspacePath, EXTENSIONS_DIR, '{*,@*/*}'))
|
|
207
|
+
];
|
|
208
|
+
const moduleFiles = [...new Set(packageDirs.flatMap(dir => glob.sync(`${dir}/**/*.control`)))].filter((file) => !/node_modules/.test(file)).sort((a, b) => a.localeCompare(b));
|
|
201
209
|
// Group files by module name to handle collisions
|
|
202
210
|
const filesByName = new Map();
|
|
203
211
|
moduleFiles.forEach((file) => {
|
|
@@ -751,21 +759,31 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
751
759
|
}
|
|
752
760
|
}
|
|
753
761
|
/**
|
|
754
|
-
* Installs an extension npm package into the
|
|
755
|
-
*
|
|
762
|
+
* Installs an extension npm package into the workspace extensions directory.
|
|
763
|
+
*
|
|
764
|
+
* When run inside a module, the installed version is recorded in the
|
|
765
|
+
* module's package.json dependencies and its .control requires list.
|
|
766
|
+
* When run at the workspace root, the installed version is recorded in
|
|
767
|
+
* the workspace pgpm.json `dependencies` field instead — no .control
|
|
768
|
+
* file is needed.
|
|
756
769
|
*/
|
|
757
770
|
async installModules(...pkgstrs) {
|
|
758
771
|
this.ensureWorkspace();
|
|
759
|
-
this.
|
|
772
|
+
const inModule = this.isInModule();
|
|
760
773
|
const originalDir = process.cwd();
|
|
761
774
|
const skitchExtDir = path.join(this.workspacePath, EXTENSIONS_DIR);
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
775
|
+
let pkgJsonPath;
|
|
776
|
+
let pkgData;
|
|
777
|
+
if (inModule) {
|
|
778
|
+
pkgJsonPath = path.join(this.modulePath, 'package.json');
|
|
779
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
780
|
+
throw new Error(`No package.json found at module path: ${this.modulePath}`);
|
|
781
|
+
}
|
|
782
|
+
pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
783
|
+
pkgData.dependencies = pkgData.dependencies || {};
|
|
765
784
|
}
|
|
766
|
-
const pkgData = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
767
|
-
pkgData.dependencies = pkgData.dependencies || {};
|
|
768
785
|
const newlyAdded = [];
|
|
786
|
+
const installedVersions = {};
|
|
769
787
|
for (const pkgstr of pkgstrs) {
|
|
770
788
|
const { name } = parse(pkgstr);
|
|
771
789
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgpm-install-'));
|
|
@@ -798,7 +816,10 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
798
816
|
throw new Error(`Missing package.json in installed extension: ${dst}`);
|
|
799
817
|
}
|
|
800
818
|
const { version } = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf-8'));
|
|
801
|
-
|
|
819
|
+
installedVersions[name] = `${version}`;
|
|
820
|
+
if (pkgData) {
|
|
821
|
+
pkgData.dependencies[name] = `${version}`;
|
|
822
|
+
}
|
|
802
823
|
const extensionName = getExtensionName(dst);
|
|
803
824
|
newlyAdded.push(extensionName);
|
|
804
825
|
}
|
|
@@ -808,20 +829,80 @@ ${dependencies.length > 0 ? dependencies.map(dep => `-- requires: ${dep}`).join(
|
|
|
808
829
|
process.chdir(originalDir);
|
|
809
830
|
}
|
|
810
831
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
832
|
+
if (inModule && pkgData && pkgJsonPath) {
|
|
833
|
+
const { dependencies, devDependencies, ...rest } = pkgData;
|
|
834
|
+
const finalPkgData = { ...rest };
|
|
835
|
+
if (dependencies) {
|
|
836
|
+
finalPkgData.dependencies = sortObjectByKey(dependencies);
|
|
837
|
+
}
|
|
838
|
+
if (devDependencies) {
|
|
839
|
+
finalPkgData.devDependencies = sortObjectByKey(devDependencies);
|
|
840
|
+
}
|
|
841
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify(finalPkgData, null, 2));
|
|
842
|
+
logger.success(`📦 Updated package.json with: ${pkgstrs.join(', ')}`);
|
|
843
|
+
// ─── Update .control file with actual extension names ──────────────
|
|
844
|
+
const currentDeps = this.getRequiredModules();
|
|
845
|
+
const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
|
|
846
|
+
writeExtensions(this.modulePath, updatedDeps);
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
this.recordWorkspaceDependencies(installedVersions);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Returns the workspace-level pgpm module dependencies recorded in
|
|
854
|
+
* the workspace pgpm.json `dependencies` field.
|
|
855
|
+
*/
|
|
856
|
+
getWorkspaceDependencies() {
|
|
857
|
+
this.ensureWorkspace();
|
|
858
|
+
const configPath = path.join(this.workspacePath, 'pgpm.json');
|
|
859
|
+
if (!fs.existsSync(configPath)) {
|
|
860
|
+
return {};
|
|
861
|
+
}
|
|
862
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
863
|
+
return config.dependencies || {};
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Records installed module versions into the workspace pgpm.json
|
|
867
|
+
* `dependencies` field. No-op when the workspace uses pgpm.config.js.
|
|
868
|
+
*/
|
|
869
|
+
recordWorkspaceDependencies(versions) {
|
|
870
|
+
this.ensureWorkspace();
|
|
871
|
+
if (Object.keys(versions).length === 0)
|
|
872
|
+
return;
|
|
873
|
+
const configPath = path.join(this.workspacePath, 'pgpm.json');
|
|
874
|
+
if (!fs.existsSync(configPath)) {
|
|
875
|
+
logger.warn('No pgpm.json found at workspace root — skipping dependency recording.');
|
|
876
|
+
return;
|
|
815
877
|
}
|
|
816
|
-
|
|
817
|
-
|
|
878
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
879
|
+
config.dependencies = sortObjectByKey({
|
|
880
|
+
...(config.dependencies || {}),
|
|
881
|
+
...versions
|
|
882
|
+
});
|
|
883
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
884
|
+
logger.success(`📦 Updated pgpm.json dependencies with: ${Object.keys(versions).join(', ')}`);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Installs the workspace-level dependencies declared in pgpm.json into
|
|
888
|
+
* the workspace extensions/ directory. Skips modules already present
|
|
889
|
+
* unless `force` is set.
|
|
890
|
+
*
|
|
891
|
+
* @returns The list of installed package specs (name@version)
|
|
892
|
+
*/
|
|
893
|
+
async installWorkspaceDependencies(options) {
|
|
894
|
+
this.ensureWorkspace();
|
|
895
|
+
const { force = false } = options || {};
|
|
896
|
+
const dependencies = this.getWorkspaceDependencies();
|
|
897
|
+
const installedModules = this.getWorkspaceInstalledModules();
|
|
898
|
+
const toInstall = Object.entries(dependencies)
|
|
899
|
+
.filter(([name]) => force || !installedModules.includes(name))
|
|
900
|
+
.map(([name, version]) => `${name}@${version}`);
|
|
901
|
+
if (toInstall.length === 0) {
|
|
902
|
+
return [];
|
|
818
903
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
// ─── Update .control file with actual extension names ──────────────
|
|
822
|
-
const currentDeps = this.getRequiredModules();
|
|
823
|
-
const updatedDeps = Array.from(new Set([...currentDeps, ...newlyAdded])).sort();
|
|
824
|
-
writeExtensions(this.modulePath, updatedDeps);
|
|
904
|
+
await this.installModules(...toInstall);
|
|
905
|
+
return toInstall;
|
|
825
906
|
}
|
|
826
907
|
/**
|
|
827
908
|
* Returns information about which of the specified modules are installed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpmjs/core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.32.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PGPM Package and Migration Tools",
|
|
6
6
|
"main": "index.js",
|
|
@@ -48,21 +48,21 @@
|
|
|
48
48
|
"makage": "^0.3.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@pgpmjs/env": "^2.28.
|
|
51
|
+
"@pgpmjs/env": "^2.28.1",
|
|
52
52
|
"@pgpmjs/logger": "^2.14.0",
|
|
53
|
-
"@pgpmjs/server-utils": "^3.15.
|
|
54
|
-
"@pgpmjs/types": "^2.
|
|
53
|
+
"@pgpmjs/server-utils": "^3.15.2",
|
|
54
|
+
"@pgpmjs/types": "^2.35.0",
|
|
55
55
|
"csv-to-pg": "^3.21.0",
|
|
56
56
|
"genomic": "^5.6.2",
|
|
57
57
|
"glob": "^13.0.6",
|
|
58
58
|
"minimatch": "^10.2.5",
|
|
59
59
|
"parse-package-name": "^1.0.0",
|
|
60
60
|
"pg": "^8.21.0",
|
|
61
|
-
"pg-cache": "^3.15.
|
|
61
|
+
"pg-cache": "^3.15.1",
|
|
62
62
|
"pg-env": "^1.18.0",
|
|
63
63
|
"pgsql-deparser": "^17.18.3",
|
|
64
64
|
"pgsql-parser": "^17.9.15",
|
|
65
65
|
"yanse": "^0.2.1"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "dd2accb683fe20c7278a0d58fd36a04a3b07ac63"
|
|
68
68
|
}
|