winstore-startup 0.1.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/LICENSE +21 -0
- package/README.md +218 -0
- package/binding.gyp +28 -0
- package/index.d.ts +41 -0
- package/index.js +85 -0
- package/lib/binding.js +18 -0
- package/package.json +67 -0
- package/prebuilds/win32-x64/winstore-startup.node +0 -0
- package/startup_task.cpp +276 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Raghvendra Yadav
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# winstore-startup
|
|
2
|
+
|
|
3
|
+
A Node.js native addon for managing Windows Store/UWP application startup tasks. This library provides programmatic control over whether your application automatically launches when Windows starts.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Enable/disable startup tasks for Windows Store apps
|
|
8
|
+
- Query current startup task state
|
|
9
|
+
- Non-blocking async API (Promise-based)
|
|
10
|
+
- Optional task ID (automatically uses first task if not specified)
|
|
11
|
+
- TypeScript support with full type definitions
|
|
12
|
+
- Graceful error handling with descriptive messages
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- **OS:** Windows only
|
|
17
|
+
- **Node.js:** >= 20.0.0
|
|
18
|
+
- **Architecture:** x64 or arm64
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install winstore-startup
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The native addon is automatically compiled during installation.
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
Before using this library, you must declare a startup task in your `AppxManifest.xml`. The library only manages existing startup tasks - it doesn't create them.
|
|
31
|
+
|
|
32
|
+
### AppxManifest.xml Configuration
|
|
33
|
+
|
|
34
|
+
Add the `desktop` namespace to your Package element:
|
|
35
|
+
|
|
36
|
+
```xml
|
|
37
|
+
<Package
|
|
38
|
+
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
|
39
|
+
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
|
40
|
+
...>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then add the startup task extension inside your Application element:
|
|
44
|
+
|
|
45
|
+
```xml
|
|
46
|
+
<Applications>
|
|
47
|
+
<Application Id="MyApp" Executable="app\MyApp.exe" EntryPoint="Windows.FullTrustApplication">
|
|
48
|
+
...
|
|
49
|
+
<Extensions>
|
|
50
|
+
<desktop:Extension
|
|
51
|
+
Category="windows.startupTask"
|
|
52
|
+
Executable="app\MyApp.exe"
|
|
53
|
+
EntryPoint="Windows.FullTrustApplication">
|
|
54
|
+
<desktop:StartupTask TaskId="MyStartupTask" Enabled="false" DisplayName="My App" />
|
|
55
|
+
</desktop:Extension>
|
|
56
|
+
</Extensions>
|
|
57
|
+
</Application>
|
|
58
|
+
</Applications>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Note:** The `TaskId` is what you pass to `enable('MyStartupTask')`. If you only have one task, you can call `enable()` without arguments and it will auto-select it.
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
### Basic Example
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const { enable, disable, getState } = require('winstore-startup');
|
|
69
|
+
|
|
70
|
+
async function manageStartup() {
|
|
71
|
+
try {
|
|
72
|
+
// Enable auto-launch (uses first startup task automatically)
|
|
73
|
+
await enable();
|
|
74
|
+
|
|
75
|
+
// Check current state
|
|
76
|
+
const state = await getState();
|
|
77
|
+
console.log(state); // 2 = Enabled
|
|
78
|
+
|
|
79
|
+
// Disable auto-launch
|
|
80
|
+
await disable();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Failed to manage startup:', error.message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
manageStartup();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### With Specific Task ID
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
const { enable, disable, getState } = require('winstore-startup');
|
|
93
|
+
|
|
94
|
+
async function manageSpecificTask() {
|
|
95
|
+
// Enable a specific task
|
|
96
|
+
await enable('MyTaskId');
|
|
97
|
+
|
|
98
|
+
// Get state of a specific task
|
|
99
|
+
const state = await getState('MyTaskId');
|
|
100
|
+
|
|
101
|
+
// Disable a specific task
|
|
102
|
+
await disable('MyTaskId');
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### List All Startup Tasks
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const { getForCurrentPackage, StartupTaskState } = require('winstore-startup');
|
|
110
|
+
|
|
111
|
+
async function listTasks() {
|
|
112
|
+
const tasks = await getForCurrentPackage();
|
|
113
|
+
|
|
114
|
+
for (const task of tasks) {
|
|
115
|
+
console.log(`Task: ${task.taskId}, State: ${task.state}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### TypeScript
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import {
|
|
124
|
+
enable,
|
|
125
|
+
disable,
|
|
126
|
+
getState,
|
|
127
|
+
getForCurrentPackage,
|
|
128
|
+
StartupTaskState,
|
|
129
|
+
} from 'winstore-startup';
|
|
130
|
+
|
|
131
|
+
async function manageStartup(): Promise<void> {
|
|
132
|
+
try {
|
|
133
|
+
// Enable startup (uses first task if no ID provided)
|
|
134
|
+
const result = await enable();
|
|
135
|
+
if (result === StartupTaskState.Enabled) {
|
|
136
|
+
console.log('Startup enabled');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// With specific task ID
|
|
140
|
+
await enable('myTaskId');
|
|
141
|
+
|
|
142
|
+
// Get all tasks
|
|
143
|
+
const tasks = await getForCurrentPackage();
|
|
144
|
+
console.log(tasks);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Operation failed:', error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## API Reference
|
|
152
|
+
|
|
153
|
+
### Functions
|
|
154
|
+
|
|
155
|
+
| Function | Description | Returns |
|
|
156
|
+
| ------------------------ | --------------------------------- | ---------------------------- |
|
|
157
|
+
| `enable(taskId?)` | Enable a startup task | `Promise<StartupTaskState>` |
|
|
158
|
+
| `disable(taskId?)` | Disable a startup task | `Promise<void>` |
|
|
159
|
+
| `getState(taskId?)` | Get the current state of a task | `Promise<StartupTaskState>` |
|
|
160
|
+
| `getForCurrentPackage()` | Get all startup tasks for the app | `Promise<StartupTaskInfo[]>` |
|
|
161
|
+
|
|
162
|
+
All functions return Promises and accept an optional `taskId` parameter (except `getForCurrentPackage`). If `taskId` is not provided, the first startup task from the current package is used automatically.
|
|
163
|
+
|
|
164
|
+
### StartupTaskState Enum
|
|
165
|
+
|
|
166
|
+
| Value | Name | Description |
|
|
167
|
+
| ----- | ------------------ | --------------------------------- |
|
|
168
|
+
| 0 | `Disabled` | Task is disabled |
|
|
169
|
+
| 1 | `DisabledByUser` | Task was disabled by the user |
|
|
170
|
+
| 2 | `Enabled` | Task is enabled |
|
|
171
|
+
| 3 | `DisabledByPolicy` | Task is disabled by system policy |
|
|
172
|
+
| 4 | `EnabledByPolicy` | Task is enabled by system policy |
|
|
173
|
+
|
|
174
|
+
### StartupTaskInfo Interface
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
interface StartupTaskInfo {
|
|
178
|
+
taskId: string;
|
|
179
|
+
state: StartupTaskState;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Error Handling
|
|
184
|
+
|
|
185
|
+
All functions throw errors with descriptive messages when:
|
|
186
|
+
|
|
187
|
+
- No startup task is found in the app manifest
|
|
188
|
+
- The specified task ID doesn't exist
|
|
189
|
+
- The operation fails at the Windows API level
|
|
190
|
+
- Running on a non-Windows platform
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
try {
|
|
194
|
+
await enable();
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// Handle error - e.g., "No startup task found. Ensure your app manifest includes a startup task declaration."
|
|
197
|
+
console.error(error.message);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## How It Works
|
|
202
|
+
|
|
203
|
+
This library uses the Windows Runtime (WinRT) `StartupTask` API to manage application startup behavior. It compiles a native C++ addon using Node-API that bridges JavaScript to the Windows APIs.
|
|
204
|
+
|
|
205
|
+
All operations are non-blocking - they run on a separate thread and return Promises, ensuring your Node.js event loop stays responsive.
|
|
206
|
+
|
|
207
|
+
On non-Windows platforms, the functions reject with an error message indicating the platform is not supported.
|
|
208
|
+
|
|
209
|
+
## Scripts
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
npm run build # Rebuild the native addon
|
|
213
|
+
npm run clean # Clean build artifacts
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
package/binding.gyp
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "winstore_startup",
|
|
5
|
+
"cflags!": ["-fno-exceptions"],
|
|
6
|
+
"cflags_cc!": ["-fno-exceptions"],
|
|
7
|
+
"sources": ["startup_task.cpp"],
|
|
8
|
+
"include_dirs": [
|
|
9
|
+
"<!@(node -p \"require('node-addon-api').include\")"
|
|
10
|
+
],
|
|
11
|
+
"conditions": [
|
|
12
|
+
[
|
|
13
|
+
"OS=='win'",
|
|
14
|
+
{
|
|
15
|
+
"msvs_settings": {
|
|
16
|
+
"VCCLCompilerTool": {
|
|
17
|
+
"AdditionalOptions": ["/std:c++20"],
|
|
18
|
+
"ExceptionHandling": 1
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"msvs_version": "auto",
|
|
22
|
+
"libraries": ["windowsapp.lib"]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export enum StartupTaskState {
|
|
2
|
+
Disabled = 0,
|
|
3
|
+
DisabledByUser = 1,
|
|
4
|
+
Enabled = 2,
|
|
5
|
+
DisabledByPolicy = 3,
|
|
6
|
+
EnabledByPolicy = 4,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface StartupTaskInfo {
|
|
10
|
+
taskId: string;
|
|
11
|
+
state: StartupTaskState;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enable a startup task
|
|
16
|
+
* @param taskId - The startup task ID (optional, uses first task if not provided)
|
|
17
|
+
* @returns The resulting state
|
|
18
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
19
|
+
*/
|
|
20
|
+
export function enable(taskId?: string): Promise<StartupTaskState>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Disable a startup task
|
|
24
|
+
* @param taskId - The startup task ID (optional, uses first task if not provided)
|
|
25
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
26
|
+
*/
|
|
27
|
+
export function disable(taskId?: string): Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get state of a startup task
|
|
31
|
+
* @param taskId - The startup task ID (optional, uses first task if not provided)
|
|
32
|
+
* @returns The current state
|
|
33
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
34
|
+
*/
|
|
35
|
+
export function getState(taskId?: string): Promise<StartupTaskState>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get all startup tasks for the current package
|
|
39
|
+
* @returns Array of startup task information
|
|
40
|
+
*/
|
|
41
|
+
export function getForCurrentPackage(): Promise<StartupTaskInfo[]>;
|
package/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const binding = global.__WINSTORE_STARTUP_MOCK_BINDING__ || require('node-gyp-build')(__dirname);
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Startup task states
|
|
5
|
+
*/
|
|
6
|
+
const StartupTaskState = {
|
|
7
|
+
Disabled: 0,
|
|
8
|
+
DisabledByUser: 1,
|
|
9
|
+
Enabled: 2,
|
|
10
|
+
DisabledByPolicy: 3,
|
|
11
|
+
EnabledByPolicy: 4,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the first task ID from the current package
|
|
16
|
+
* @returns {Promise<string|null>}
|
|
17
|
+
*/
|
|
18
|
+
async function getFirstTaskId() {
|
|
19
|
+
const tasks = await binding.getForCurrentPackage();
|
|
20
|
+
return tasks.length > 0 ? tasks[0].taskId : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enable a startup task
|
|
25
|
+
* @param {string} [taskId] - The startup task ID (optional, uses first task if not provided)
|
|
26
|
+
* @returns {Promise<number>} The resulting state
|
|
27
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
28
|
+
*/
|
|
29
|
+
async function enable(taskId) {
|
|
30
|
+
const id = taskId || (await getFirstTaskId());
|
|
31
|
+
if (!id) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'No startup task found. Ensure your app manifest includes a startup task declaration.',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return binding.enable(id);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Disable a startup task
|
|
41
|
+
* @param {string} [taskId] - The startup task ID (optional, uses first task if not provided)
|
|
42
|
+
* @returns {Promise<void>}
|
|
43
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
44
|
+
*/
|
|
45
|
+
async function disable(taskId) {
|
|
46
|
+
const id = taskId || (await getFirstTaskId());
|
|
47
|
+
if (!id) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'No startup task found. Ensure your app manifest includes a startup task declaration.',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return binding.disable(id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get state of a startup task
|
|
57
|
+
* @param {string} [taskId] - The startup task ID (optional, uses first task if not provided)
|
|
58
|
+
* @returns {Promise<number>} The current state
|
|
59
|
+
* @throws {Error} If no startup task is found or operation fails
|
|
60
|
+
*/
|
|
61
|
+
async function getState(taskId) {
|
|
62
|
+
const id = taskId || (await getFirstTaskId());
|
|
63
|
+
if (!id) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'No startup task found. Ensure your app manifest includes a startup task declaration.',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return binding.getState(id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get all startup tasks for the current package
|
|
73
|
+
* @returns {Promise<Array<{taskId: string, state: number}>>}
|
|
74
|
+
*/
|
|
75
|
+
async function getForCurrentPackage() {
|
|
76
|
+
return binding.getForCurrentPackage();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
enable,
|
|
81
|
+
disable,
|
|
82
|
+
getState,
|
|
83
|
+
getForCurrentPackage,
|
|
84
|
+
StartupTaskState,
|
|
85
|
+
};
|
package/lib/binding.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Allow tests to inject a mock binding
|
|
2
|
+
let binding = global.__WINSTORE_STARTUP_MOCK_BINDING__;
|
|
3
|
+
|
|
4
|
+
if (!binding) {
|
|
5
|
+
try {
|
|
6
|
+
binding = require('../build/Release/winstore_startup.node');
|
|
7
|
+
} catch (e) {
|
|
8
|
+
// Fallback for development or non-Windows platforms
|
|
9
|
+
binding = {
|
|
10
|
+
enable: () => Promise.reject(new Error('Not supported on this platform')),
|
|
11
|
+
disable: () => Promise.reject(new Error('Not supported on this platform')),
|
|
12
|
+
getState: () => Promise.reject(new Error('Not supported on this platform')),
|
|
13
|
+
getForCurrentPackage: () => Promise.resolve([]),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = binding;
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "winstore-startup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Windows Store startup task management using C++/WinRT",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"addon",
|
|
7
|
+
"autostart",
|
|
8
|
+
"native",
|
|
9
|
+
"startup",
|
|
10
|
+
"startup-task",
|
|
11
|
+
"uwp",
|
|
12
|
+
"windows",
|
|
13
|
+
"windows-store",
|
|
14
|
+
"winrt"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Raghvendra Yadav",
|
|
19
|
+
"email": "raghvendraa.dev@gmail.com"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/aspect-dev/winstore-startup.git"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"index.js",
|
|
27
|
+
"index.d.ts",
|
|
28
|
+
"startup_task.cpp",
|
|
29
|
+
"binding.gyp",
|
|
30
|
+
"lib",
|
|
31
|
+
"prebuilds"
|
|
32
|
+
],
|
|
33
|
+
"os": [
|
|
34
|
+
"win32"
|
|
35
|
+
],
|
|
36
|
+
"cpu": [
|
|
37
|
+
"x64",
|
|
38
|
+
"arm64"
|
|
39
|
+
],
|
|
40
|
+
"main": "index.js",
|
|
41
|
+
"types": "index.d.ts",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"install": "node-gyp-build",
|
|
44
|
+
"prebuild": "prebuildify --napi --strip",
|
|
45
|
+
"prebuild:all": "prebuildify --napi --strip --arch x64 && prebuildify --napi --strip --arch arm64",
|
|
46
|
+
"fmt": "oxfmt",
|
|
47
|
+
"fmt:check": "oxfmt --check",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:coverage": "vitest run --coverage",
|
|
51
|
+
"clean": "git clean -fx ./build ./prebuilds ./node_modules"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"node-addon-api": "^8.5.0",
|
|
55
|
+
"node-gyp-build": "^4.8.4"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"oxfmt": "^0.36.0",
|
|
59
|
+
"oxlint": "^1.51.0",
|
|
60
|
+
"prebuildify": "^6.0.1",
|
|
61
|
+
"vitest": "^4.0.18"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=20.0.0"
|
|
65
|
+
},
|
|
66
|
+
"gypfile": true
|
|
67
|
+
}
|
|
Binary file
|
package/startup_task.cpp
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// startup_task.cpp - WinRT-based startup task addon for Windows Store apps
|
|
2
|
+
#include <napi.h>
|
|
3
|
+
#include <optional>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
#ifdef _WIN32
|
|
7
|
+
#include <winrt/Windows.ApplicationModel.h>
|
|
8
|
+
#include <winrt/Windows.Foundation.h>
|
|
9
|
+
#include <winrt/Windows.Foundation.Collections.h>
|
|
10
|
+
|
|
11
|
+
using namespace winrt;
|
|
12
|
+
using namespace Windows::ApplicationModel;
|
|
13
|
+
using namespace Windows::Foundation;
|
|
14
|
+
|
|
15
|
+
// Initialize WinRT apartment on module load
|
|
16
|
+
struct WinRTInitializer {
|
|
17
|
+
WinRTInitializer() {
|
|
18
|
+
winrt::init_apartment();
|
|
19
|
+
}
|
|
20
|
+
~WinRTInitializer() {
|
|
21
|
+
winrt::uninit_apartment();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
static WinRTInitializer g_winrt_init;
|
|
26
|
+
|
|
27
|
+
// Startup task states (matches Windows.ApplicationModel.StartupTaskState)
|
|
28
|
+
// 0 = Disabled, 1 = DisabledByUser, 2 = Enabled, 3 = DisabledByPolicy, 4 = EnabledByPolicy
|
|
29
|
+
|
|
30
|
+
// Helper function to validate and extract taskId parameter
|
|
31
|
+
std::optional<std::string> GetTaskIdParam(const Napi::CallbackInfo& info) {
|
|
32
|
+
auto env = info.Env();
|
|
33
|
+
|
|
34
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
35
|
+
Napi::TypeError::New(env, "Task ID string expected").ThrowAsJavaScriptException();
|
|
36
|
+
return std::nullopt;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
std::string taskId = info[0].As<Napi::String>().Utf8Value();
|
|
40
|
+
if (taskId.empty()) {
|
|
41
|
+
Napi::TypeError::New(env, "Task ID cannot be empty").ThrowAsJavaScriptException();
|
|
42
|
+
return std::nullopt;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return taskId;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Async worker for Enable operation
|
|
49
|
+
class EnableWorker : public Napi::AsyncWorker {
|
|
50
|
+
public:
|
|
51
|
+
EnableWorker(Napi::Env env, Napi::Promise::Deferred deferred, std::string taskId)
|
|
52
|
+
: Napi::AsyncWorker(env), deferred_(deferred), taskId_(taskId), state_(-1) {}
|
|
53
|
+
|
|
54
|
+
void Execute() override {
|
|
55
|
+
try {
|
|
56
|
+
auto task = StartupTask::GetAsync(winrt::to_hstring(taskId_)).get();
|
|
57
|
+
state_ = static_cast<int>(task.RequestEnableAsync().get());
|
|
58
|
+
} catch (const winrt::hresult_error& e) {
|
|
59
|
+
SetError(winrt::to_string(e.message()));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void OnOK() override {
|
|
64
|
+
deferred_.Resolve(Napi::Number::New(Env(), state_));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void OnError(const Napi::Error& error) override {
|
|
68
|
+
deferred_.Reject(error.Value());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private:
|
|
72
|
+
Napi::Promise::Deferred deferred_;
|
|
73
|
+
std::string taskId_;
|
|
74
|
+
int state_;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Async worker for Disable operation
|
|
78
|
+
class DisableWorker : public Napi::AsyncWorker {
|
|
79
|
+
public:
|
|
80
|
+
DisableWorker(Napi::Env env, Napi::Promise::Deferred deferred, std::string taskId)
|
|
81
|
+
: Napi::AsyncWorker(env), deferred_(deferred), taskId_(taskId) {}
|
|
82
|
+
|
|
83
|
+
void Execute() override {
|
|
84
|
+
try {
|
|
85
|
+
auto task = StartupTask::GetAsync(winrt::to_hstring(taskId_)).get();
|
|
86
|
+
task.Disable();
|
|
87
|
+
} catch (const winrt::hresult_error& e) {
|
|
88
|
+
SetError(winrt::to_string(e.message()));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void OnOK() override {
|
|
93
|
+
deferred_.Resolve(Env().Undefined());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void OnError(const Napi::Error& error) override {
|
|
97
|
+
deferred_.Reject(error.Value());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private:
|
|
101
|
+
Napi::Promise::Deferred deferred_;
|
|
102
|
+
std::string taskId_;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Async worker for GetState operation
|
|
106
|
+
class GetStateWorker : public Napi::AsyncWorker {
|
|
107
|
+
public:
|
|
108
|
+
GetStateWorker(Napi::Env env, Napi::Promise::Deferred deferred, std::string taskId)
|
|
109
|
+
: Napi::AsyncWorker(env), deferred_(deferred), taskId_(taskId), state_(-1) {}
|
|
110
|
+
|
|
111
|
+
void Execute() override {
|
|
112
|
+
try {
|
|
113
|
+
auto task = StartupTask::GetAsync(winrt::to_hstring(taskId_)).get();
|
|
114
|
+
state_ = static_cast<int>(task.State());
|
|
115
|
+
} catch (const winrt::hresult_error& e) {
|
|
116
|
+
SetError(winrt::to_string(e.message()));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
void OnOK() override {
|
|
121
|
+
deferred_.Resolve(Napi::Number::New(Env(), state_));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void OnError(const Napi::Error& error) override {
|
|
125
|
+
deferred_.Reject(error.Value());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private:
|
|
129
|
+
Napi::Promise::Deferred deferred_;
|
|
130
|
+
std::string taskId_;
|
|
131
|
+
int state_;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Async worker for GetForCurrentPackage operation
|
|
135
|
+
struct TaskInfo {
|
|
136
|
+
std::string taskId;
|
|
137
|
+
int state;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
class GetForCurrentPackageWorker : public Napi::AsyncWorker {
|
|
141
|
+
public:
|
|
142
|
+
GetForCurrentPackageWorker(Napi::Env env, Napi::Promise::Deferred deferred)
|
|
143
|
+
: Napi::AsyncWorker(env), deferred_(deferred) {}
|
|
144
|
+
|
|
145
|
+
void Execute() override {
|
|
146
|
+
try {
|
|
147
|
+
auto tasks = StartupTask::GetForCurrentPackageAsync().get();
|
|
148
|
+
for (auto const& task : tasks) {
|
|
149
|
+
TaskInfo info;
|
|
150
|
+
info.taskId = winrt::to_string(task.TaskId());
|
|
151
|
+
info.state = static_cast<int>(task.State());
|
|
152
|
+
tasks_.push_back(info);
|
|
153
|
+
}
|
|
154
|
+
} catch (const winrt::hresult_error& e) {
|
|
155
|
+
SetError(winrt::to_string(e.message()));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
void OnOK() override {
|
|
160
|
+
auto env = Env();
|
|
161
|
+
auto arr = Napi::Array::New(env, tasks_.size());
|
|
162
|
+
|
|
163
|
+
for (size_t i = 0; i < tasks_.size(); i++) {
|
|
164
|
+
auto obj = Napi::Object::New(env);
|
|
165
|
+
obj.Set("taskId", Napi::String::New(env, tasks_[i].taskId));
|
|
166
|
+
obj.Set("state", Napi::Number::New(env, tasks_[i].state));
|
|
167
|
+
arr[static_cast<uint32_t>(i)] = obj;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
deferred_.Resolve(arr);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void OnError(const Napi::Error& error) override {
|
|
174
|
+
deferred_.Reject(error.Value());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private:
|
|
178
|
+
Napi::Promise::Deferred deferred_;
|
|
179
|
+
std::vector<TaskInfo> tasks_;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
Napi::Value Enable(const Napi::CallbackInfo& info) {
|
|
183
|
+
auto env = info.Env();
|
|
184
|
+
auto taskIdOpt = GetTaskIdParam(info);
|
|
185
|
+
|
|
186
|
+
if (!taskIdOpt.has_value()) {
|
|
187
|
+
return env.Undefined();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
191
|
+
auto worker = new EnableWorker(env, deferred, taskIdOpt.value());
|
|
192
|
+
worker->Queue();
|
|
193
|
+
|
|
194
|
+
return deferred.Promise();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Napi::Value Disable(const Napi::CallbackInfo& info) {
|
|
198
|
+
auto env = info.Env();
|
|
199
|
+
auto taskIdOpt = GetTaskIdParam(info);
|
|
200
|
+
|
|
201
|
+
if (!taskIdOpt.has_value()) {
|
|
202
|
+
return env.Undefined();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
206
|
+
auto worker = new DisableWorker(env, deferred, taskIdOpt.value());
|
|
207
|
+
worker->Queue();
|
|
208
|
+
|
|
209
|
+
return deferred.Promise();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
Napi::Value GetState(const Napi::CallbackInfo& info) {
|
|
213
|
+
auto env = info.Env();
|
|
214
|
+
auto taskIdOpt = GetTaskIdParam(info);
|
|
215
|
+
|
|
216
|
+
if (!taskIdOpt.has_value()) {
|
|
217
|
+
return env.Undefined();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
221
|
+
auto worker = new GetStateWorker(env, deferred, taskIdOpt.value());
|
|
222
|
+
worker->Queue();
|
|
223
|
+
|
|
224
|
+
return deferred.Promise();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
Napi::Value GetForCurrentPackage(const Napi::CallbackInfo& info) {
|
|
228
|
+
auto env = info.Env();
|
|
229
|
+
|
|
230
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
231
|
+
auto worker = new GetForCurrentPackageWorker(env, deferred);
|
|
232
|
+
worker->Queue();
|
|
233
|
+
|
|
234
|
+
return deferred.Promise();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
#else
|
|
238
|
+
// Non-Windows stubs (return rejected promises for consistency)
|
|
239
|
+
Napi::Value Enable(const Napi::CallbackInfo& info) {
|
|
240
|
+
auto env = info.Env();
|
|
241
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
242
|
+
deferred.Reject(Napi::Error::New(env, "Not supported on this platform").Value());
|
|
243
|
+
return deferred.Promise();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
Napi::Value Disable(const Napi::CallbackInfo& info) {
|
|
247
|
+
auto env = info.Env();
|
|
248
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
249
|
+
deferred.Reject(Napi::Error::New(env, "Not supported on this platform").Value());
|
|
250
|
+
return deferred.Promise();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
Napi::Value GetState(const Napi::CallbackInfo& info) {
|
|
254
|
+
auto env = info.Env();
|
|
255
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
256
|
+
deferred.Reject(Napi::Error::New(env, "Not supported on this platform").Value());
|
|
257
|
+
return deferred.Promise();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
Napi::Value GetForCurrentPackage(const Napi::CallbackInfo& info) {
|
|
261
|
+
auto env = info.Env();
|
|
262
|
+
auto deferred = Napi::Promise::Deferred::New(env);
|
|
263
|
+
deferred.Resolve(Napi::Array::New(env, 0));
|
|
264
|
+
return deferred.Promise();
|
|
265
|
+
}
|
|
266
|
+
#endif
|
|
267
|
+
|
|
268
|
+
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
269
|
+
exports.Set("enable", Napi::Function::New(env, Enable));
|
|
270
|
+
exports.Set("disable", Napi::Function::New(env, Disable));
|
|
271
|
+
exports.Set("getState", Napi::Function::New(env, GetState));
|
|
272
|
+
exports.Set("getForCurrentPackage", Napi::Function::New(env, GetForCurrentPackage));
|
|
273
|
+
return exports;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
NODE_API_MODULE(winstore_startup, Init)
|